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 diff --git a/ap2_test.py b/ap2_test.py index 7deb90c..66dc36e 100644 --- a/ap2_test.py +++ b/ap2_test.py @@ -88,6 +88,56 @@ def test_ap2_mandate_completion(self) -> None: msg="Checkout status not 'completed'", ) + def test_ap2_mandate_invalid_signature(self) -> None: + """Test checkout completion failure with invalid AP2 signature. + + Given a ready-to-complete checkout session, + When a completion request is made with an invalid ap2 mandate signature, + Then the request should fail with status 400 and error code + 'mandate_invalid_signature'. + """ + response_json = self.create_checkout_session() + checkout_id = checkout.Checkout(**response_json).id + + credential = token_credential_resp.TokenCredentialResponse( + type="token", token="success_token" + ) + instr = payment_instrument.PaymentInstrument( + root=card_payment_instrument.CardPaymentInstrument( + id="instr_1", + brand="visa", + last_digits="4242", + handler_id="mock_payment_handler", + handler_name="mock_payment_handler", + type="card", + credential=credential, + ) + ) + payment_data = instr.root.model_dump(mode="json", exclude_none=True) + + # Use trigger string for mock verification failure + mandate = CheckoutMandate(root="header.payload.invalid_signature~kb_sig") + ap2_data = Ap2CompleteRequest(checkout_mandate=mandate) + + payment_payload = { + "payment_data": payment_data, + "risk_signals": {}, + "ap2": ap2_data.model_dump(mode="json", exclude_none=True), + } + + response = self.client.post( + f"/checkout-sessions/{checkout_id}/complete", + json=payment_payload, + headers=integration_test_utils.get_headers(), + ) + + self.assert_response_status(response, 400) + self.assertEqual( + response.json().get("code"), + "mandate_invalid_signature", + msg="Error code should be 'mandate_invalid_signature'", + ) + if __name__ == "__main__": absltest.main() 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()