-
Notifications
You must be signed in to change notification settings - Fork 110
fix: Update python server /samples to UCP 01-23 version. #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
446c6a1
13a5e7f
7cfd827
dfbffcc
7678e3e
bc2ece5
9e33737
690ff38
5ab4914
2cecdb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,22 +37,11 @@ | |
| from ucp_sdk.models.schemas.shopping import checkout_create_req | ||
| from ucp_sdk.models.schemas.shopping import checkout_update_req | ||
| from ucp_sdk.models.schemas.shopping import payment_create_req | ||
| from ucp_sdk.models.schemas.shopping.payment_data import PaymentData | ||
| from ucp_sdk.models.schemas.shopping.types import buyer | ||
| from ucp_sdk.models.schemas.shopping.types import item_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import item_update_req | ||
| from ucp_sdk.models.schemas.shopping.types import line_item_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import line_item_update_req | ||
| from ucp_sdk.models.schemas.shopping.types.card_payment_instrument import ( | ||
| CardPaymentInstrument, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types.payment_instrument import ( | ||
| PaymentInstrument, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types.postal_address import PostalAddress | ||
| from ucp_sdk.models.schemas.shopping.types.token_credential_resp import ( | ||
| TokenCredentialResponse, | ||
| ) | ||
|
|
||
|
|
||
| def get_headers() -> dict[str, str]: | ||
|
|
@@ -805,48 +794,30 @@ def main() -> None: | |
|
|
||
| # Matches the structure expected by the server's updated complete_checkout | ||
|
|
||
| billing_address = PostalAddress( | ||
| street_address="123 Main St", | ||
| address_locality="Anytown", | ||
| address_region="CA", | ||
| address_country="US", | ||
| postal_code="12345", | ||
| ) | ||
|
|
||
| credential = TokenCredentialResponse(type="token", token="success_token") | ||
|
|
||
| instr = CardPaymentInstrument( | ||
| id="instr_my_card", | ||
| handler_id=target_handler, | ||
| handler_name=target_handler, | ||
| type="card", | ||
| brand="Visa", | ||
| last_digits="4242", | ||
| credential=credential, | ||
| billing_address=billing_address, | ||
| ) | ||
|
|
||
| # Wrapped in RootModel | ||
|
|
||
| wrapped_instr = PaymentInstrument(root=instr) | ||
|
|
||
| # Use PaymentData to wrap the payload | ||
|
|
||
| final_req = PaymentData(payment_data=wrapped_instr) | ||
|
|
||
| # Add risk_signals as extra fields (since it's not explicitly in the model) | ||
|
|
||
| # Using model_extra or just passing to constructor if allow_extra is true | ||
|
|
||
| # PaymentData allows extra. | ||
|
|
||
| final_payload = final_req.model_dump( | ||
| mode="json", by_alias=True, exclude_none=True | ||
| ) | ||
|
|
||
| final_payload["risk_signals"] = { | ||
| "ip": "127.0.0.1", | ||
| "browser": "python-httpx", | ||
| final_payload = { | ||
| "payment_data": { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this does not seem a v23 payload. i'd expect "payment", not "payment_data" |
||
| "id": "instr_my_card", | ||
| "handler_id": target_handler, | ||
| "handler_name": target_handler, | ||
| "type": "card", | ||
| "brand": "Visa", | ||
| "last_digits": "4242", | ||
| "credential": { | ||
| "type": "token", | ||
| "token": "success_token", | ||
| }, | ||
| "billing_address": { | ||
| "street_address": "123 Main St", | ||
| "address_locality": "Anytown", | ||
| "address_region": "CA", | ||
| "address_country": "US", | ||
| "postal_code": "12345", | ||
| }, | ||
| }, | ||
| "risk_signals": { | ||
| "ip": "127.0.0.1", | ||
| "browser": "python-httpx", | ||
| }, | ||
| } | ||
|
|
||
| headers = get_headers() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,34 +31,47 @@ | |
| from sqlalchemy.ext.asyncio import create_async_engine | ||
| from sqlalchemy.orm import sessionmaker | ||
| from sqlalchemy.sql import delete | ||
| from ucp_sdk.models.schemas.shopping import checkout_create_req | ||
| from ucp_sdk.models.schemas.shopping import payment_create_req | ||
| from ucp_sdk.models.schemas.shopping.ap2_mandate import ( | ||
| CheckoutResponseWithAp2 as Ap2Checkout, | ||
| from ucp_sdk.models.schemas.shopping import ( | ||
| checkout_create_request as checkout_create_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.buyer_consent_resp import ( | ||
| from ucp_sdk.models.schemas.shopping import ( | ||
| payment_create_request as payment_create_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.ap2_mandate import Checkout as Ap2Checkout | ||
| from ucp_sdk.models.schemas.shopping.buyer_consent import ( | ||
| Checkout as BuyerConsentCheckoutResp, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.discount_resp import ( | ||
| from ucp_sdk.models.schemas.shopping.discount import ( | ||
| Checkout as DiscountCheckoutResp, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.fulfillment_create_req import Fulfillment | ||
| from ucp_sdk.models.schemas.shopping.fulfillment_resp import ( | ||
| from ucp_sdk.models.schemas.shopping.fulfillment import ( | ||
| Checkout as Fulfillment, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.fulfillment import ( | ||
| Checkout as FulfillmentCheckout, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.order import PlatformConfig | ||
| from ucp_sdk.models.schemas.shopping.payment_data import PaymentData | ||
| from ucp_sdk.models.schemas.shopping.order import PlatformSchema | ||
| from ucp_sdk.models.schemas.shopping.types import card_payment_instrument | ||
| from ucp_sdk.models.schemas.shopping.types import fulfillment_destination_req | ||
| from ucp_sdk.models.schemas.shopping.types import fulfillment_group_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import fulfillment_method_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import fulfillment_req | ||
| from ucp_sdk.models.schemas.shopping.types import item_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import line_item_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import payment_handler_create_req | ||
| from ucp_sdk.models.schemas.shopping.types import ( | ||
| fulfillment_group_create_request as fulfillment_group_create_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types import ( | ||
| fulfillment_method_create_request as fulfillment_method_create_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types import fulfillment as fulfillment_req | ||
| from ucp_sdk.models.schemas.shopping.types import ( | ||
| item_create_request as item_create_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types import ( | ||
| line_item_create_request as line_item_create_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types import payment_instrument | ||
| from ucp_sdk.models.schemas.shopping.types import shipping_destination_req | ||
| from ucp_sdk.models.schemas.shopping.types import token_credential_resp | ||
| from ucp_sdk.models.schemas.shopping.types import ( | ||
| shipping_destination as shipping_destination_req, | ||
| ) | ||
| from ucp_sdk.models.schemas.shopping.types import ( | ||
| token_credential as token_credential_resp, | ||
| ) | ||
|
|
||
| FLAGS = flags.FLAGS | ||
|
|
||
|
|
@@ -71,7 +84,7 @@ class TestCheckout( | |
| ): | ||
| """Checkout model supporting Fulfillment, Discount, and AP2 extensions.""" | ||
|
|
||
| platform: PlatformConfig | None = None | ||
| platform: PlatformSchema | None = None | ||
|
|
||
|
|
||
| class IntegrationTest(absltest.TestCase): | ||
|
|
@@ -223,37 +236,24 @@ def _create_checkout_payload( | |
| ) | ||
| line_items.append(line_item) | ||
|
|
||
| handler = payment_handler_create_req.PaymentHandlerCreateRequest( | ||
| id="google_pay", | ||
| name="google.pay", | ||
| version="2026-01-11", | ||
| spec="https://example.com/spec", | ||
| config_schema="https://example.com/schema", | ||
| instrument_schemas=["https://example.com/schema"], | ||
| config={}, | ||
| ) | ||
|
|
||
| payment = payment_create_req.PaymentCreateRequest( | ||
| handlers=[handler], instruments=[] | ||
| ) | ||
| payment = payment_create_req.PaymentCreateRequest(instruments=[]) | ||
|
|
||
| # Hierarchical Fulfillment Construction | ||
| destination = fulfillment_destination_req.FulfillmentDestinationRequest( | ||
| root=shipping_destination_req.ShippingDestinationRequest( | ||
| id="dest_1", address_country="US" | ||
| ) | ||
| destination = shipping_destination_req.ShippingDestination( | ||
| id="dest_1", address_country="US" | ||
| ) | ||
| group = fulfillment_group_create_req.FulfillmentGroupCreateRequest( | ||
| selected_option_id="std-ship" | ||
| ) | ||
| method = fulfillment_method_create_req.FulfillmentMethodCreateRequest( | ||
| line_item_ids=[i_id for i_id, _, _, _ in items], | ||
| type="shipping", | ||
| destinations=[destination], | ||
| selected_destination_id="dest_1", | ||
| groups=[group], | ||
| ) | ||
| fulfillment = Fulfillment( | ||
| root=fulfillment_req.FulfillmentRequest(methods=[method]) | ||
| root=fulfillment_req.Fulfillment(methods=[method]) | ||
| ) | ||
|
|
||
| return checkout_create_req.CheckoutCreateRequest( | ||
|
|
@@ -264,9 +264,9 @@ def _create_checkout_payload( | |
| fulfillment=fulfillment, | ||
| ) | ||
|
|
||
| def _create_payment_payload(self) -> PaymentData: | ||
| def _create_payment_payload(self) -> dict: | ||
| """Create a payment payload using SDK models.""" | ||
| credential = token_credential_resp.TokenCredentialResponse( | ||
| credential = token_credential_resp.TokenCredential( | ||
| type="token", token="success_token" | ||
| ) | ||
| instrument = card_payment_instrument.CardPaymentInstrument( | ||
|
|
@@ -278,10 +278,12 @@ def _create_payment_payload(self) -> PaymentData: | |
| last_digits="1234", | ||
| credential=credential, | ||
| ) | ||
| return PaymentData( | ||
| payment_data=payment_instrument.PaymentInstrument(root=instrument), | ||
| risk_signals={}, | ||
| ) | ||
| return { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you explain the rationale?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The client post (lines 306-310) require the direct json object or a dictionary. They don't serialize pydantic models, so if we the method returns a class here it would need to be converted or unpacked with "model_dump" like it was before. This is cleaner as it avoids the double conversion. |
||
| "payment_data": payment_instrument.PaymentInstrument( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should use a native object, not a json
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the reply to your other comment one line above. It's for testing the post payload and it would need to be converted anyway. |
||
| root=instrument | ||
| ).model_dump(mode="json", exclude_none=True), | ||
| "risk_signals": {}, | ||
| } | ||
|
|
||
| def test_single_item_checkout(self) -> None: | ||
| """Test the full lifecycle of a single item checkout.""" | ||
|
|
@@ -305,7 +307,7 @@ def test_single_item_checkout(self) -> None: | |
| response = self.client.post( | ||
| "/checkout-sessions/test_checkout_1/complete", | ||
| headers=self._get_headers(idempotency_key="2", request_id="2"), | ||
| json=payment_payload.model_dump(mode="json", exclude_none=True), | ||
| json=payment_payload, | ||
| ) | ||
| self.assertEqual(response.status_code, 200) | ||
| checkout = TestCheckout.model_validate(response.json()) | ||
|
|
@@ -353,15 +355,15 @@ def test_double_complete_checkout(self) -> None: | |
| response = self.client.post( | ||
| "/checkout-sessions/test_checkout_double/complete", | ||
| headers=self._get_headers(idempotency_key="2", request_id="2"), | ||
| json=payment_payload.model_dump(mode="json", exclude_none=True), | ||
| json=payment_payload, | ||
| ) | ||
| self.assertEqual(response.status_code, 200) | ||
|
|
||
| # 3. Complete Checkout (Second time) - Should fail | ||
| response = self.client.post( | ||
| "/checkout-sessions/test_checkout_double/complete", | ||
| headers=self._get_headers(idempotency_key="4", request_id="4"), | ||
| json=payment_payload.model_dump(mode="json", exclude_none=True), | ||
| json=payment_payload, | ||
| ) | ||
| self.assertEqual(response.status_code, 409) | ||
| self.assertEqual( | ||
|
|
@@ -389,7 +391,7 @@ def test_multi_item_checkout(self) -> None: | |
| response = self.client.post( | ||
| "/checkout-sessions/test_checkout_multi/complete", | ||
| headers=self._get_headers(idempotency_key="6", request_id="6"), | ||
| json=payment_payload.model_dump(mode="json", exclude_none=True), | ||
| json=payment_payload, | ||
| ) | ||
| self.assertEqual(response.status_code, 200) | ||
|
|
||
|
|
@@ -480,7 +482,7 @@ def test_cancel_checkout(self) -> None: | |
| headers=self._get_headers( | ||
| idempotency_key="cancel_5", request_id="cancel_5" | ||
| ), | ||
| json=payment_payload.model_dump(mode="json", exclude_none=True), | ||
| json=payment_payload, | ||
| ) | ||
| self.assertEqual(response.status_code, 200) | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.