From b1ed4d08113ed58d694e0c56c55c67548957f2ec Mon Sep 17 00:00:00 2001 From: Florin Iucha Date: Sat, 17 Jan 2026 00:18:36 +0000 Subject: [PATCH 1/3] chore: add custom dictionary --- .cspell/custom-words.txt | 72 +++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt index 0a4b07d..bbd457d 100644 --- a/.cspell/custom-words.txt +++ b/.cspell/custom-words.txt @@ -1,78 +1,80 @@ # cspell-specific custom words related to UCP +absl +absltest +adyen Adyen +agentic Alam Amex Ant Anytown +atok +backorder Backordered Braintree Carrefour Centricity +checkout Chewy Commerce -Credentialless -Depot -EWALLET -Etsy -Flipkart -Gap -GitHub -Google -Gpay -Kroger -Lowe's -Macy's -Mastercard -Paymentech -Paypal -Preorders -Queensway -Sephora -Shopify -Shopee -Stripe -Target -UCP -Ulta -Visa -Wayfair -Worldpay -Zalando -adyen -agentic -atok -backorder -checkout -credentialless credentialization +credentialless +Credentialless datamodel +Depot dpan +Etsy ewallet +EWALLET +Flipkart fontawesome fpan fulfillable +Gap +GitHub +Google gpay +Gpay ingestions inlinehilite +Kroger linenums llmstxt +Lowe's +Macy's mastercard +Mastercard mkdocs mtok openapi openrpc +Paymentech paypal +Paypal permissionless preorders +Preorders proto protobuf pymdownx +Queensway renderable repudiable schemas sdjwt +Sephora +Shopee shopify +Shopify +Stripe superfences +Target +UCP +Ulta +Visa vulnz +Wayfair +Worldpay yaml -yml \ No newline at end of file +yml +Zalando From 0905946d9d8ff8cd8e10672d067a28516dbae9ea Mon Sep 17 00:00:00 2001 From: Florin Iucha Date: Fri, 16 Jan 2026 23:37:03 +0000 Subject: [PATCH 2/3] style: fix indentation in protocol_test.py --- protocol_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocol_test.py b/protocol_test.py index 7ce69e0..761d2bf 100644 --- a/protocol_test.py +++ b/protocol_test.py @@ -110,17 +110,17 @@ def test_version_negotiation(self): profile = UcpDiscoveryProfile(**discovery_resp.json()) shopping_service = profile.ucp.services.root["dev.ucp.shopping"] self.assertIsNotNone( - shopping_service, "Shopping service not found in discovery" + shopping_service, "Shopping service not found in discovery" ) self.assertIsNotNone( - shopping_service.rest, "REST config not found for shopping service" + shopping_service.rest, "REST config not found for shopping service" ) self.assertIsNotNone( - shopping_service.rest.endpoint, - "Endpoint not found for shopping service", + shopping_service.rest.endpoint, + "Endpoint not found for shopping service", ) checkout_sessions_url = ( - f"{str(shopping_service.rest.endpoint).rstrip('/')}/checkout-sessions" + f"{str(shopping_service.rest.endpoint).rstrip('/')}/checkout-sessions" ) create_payload = self.create_checkout_payload() From 61dddb196796652aedef6e8a154b5c73d64a022e Mon Sep 17 00:00:00 2001 From: Florin Iucha Date: Fri, 16 Jan 2026 22:06:55 +0000 Subject: [PATCH 3/3] feat: add conformance test for 'requires_escalation' via risk signals Why: - To verify that the server correctly implements the 'requires_escalation' status when risk checks fail during completion. - Ensures conformance with the checkout status lifecycle. What: - Added 'test_escalation_flow' to 'CheckoutLifecycleTest'. - Triggers escalation by sending 'simulation_trigger': 'escalation_required' in 'risk_signals' during 'complete_checkout'. - Asserts that status becomes 'requires_escalation' and 'continue_url' is present. --- checkout_lifecycle_test.py | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/checkout_lifecycle_test.py b/checkout_lifecycle_test.py index 80c2e87..d5b30fb 100644 --- a/checkout_lifecycle_test.py +++ b/checkout_lifecycle_test.py @@ -413,6 +413,46 @@ def test_cannot_cancel_completed_checkout(self): msg="Should not be able to cancel a completed checkout.", ) + def test_escalation_flow(self): + """Test checkout escalation flow. + + Given a checkout session, + When completion is attempted with a risk signal triggering escalation, + Then the status should be 'requires_escalation' and continue_url should be + present. + """ + response_json = self.create_checkout_session() + checkout_obj = checkout.Checkout(**response_json) + checkout_id = checkout_obj.id + + # Complete with risk signal + payment_payload = integration_test_utils.get_valid_payment_payload() + payment_payload["risk_signals"] = { + "simulation_trigger": "escalation_required" + } + + response = self.client.post( + f"/checkout-sessions/{checkout_id}/complete", + json=payment_payload, + headers=integration_test_utils.get_headers(), + ) + + self.assert_response_status(response, 200) + updated_checkout = checkout.Checkout(**response.json()) + + self.assertEqual( + updated_checkout.status, + "requires_escalation", + msg="Status should be requires_escalation", + ) + self.assertIsNotNone( + updated_checkout.continue_url, "continue_url should be present" + ) + self.assertTrue(updated_checkout.messages, "Messages should be present") + self.assertEqual( + updated_checkout.messages[0].root.severity, "requires_buyer_input" + ) + if __name__ == "__main__": absltest.main()