Repository navigation
Potential UCP checkout issue: complete endpoint places orders without payment verification #1
Description
Potential UCP checkout issue: complete endpoint places orders without payment verification
Hi, I noticed a possible payment-flow issue in the current repository state. This is a conservative report based on the current code path, and I may be missing deployment-specific guards outside this repository.
Reviewed HEAD: 7cca048
What I observed
The UCP complete endpoint validates only a PaymentInterface payload, uses it for billing data, and calls Magento order placement without an AP2/UCP credential or settlement check.
Source scan notes:
- scan_uncovered1000_20260604_batch25: True UCP/AP2-adjacent Magento module. The complete checkout endpoint validates only PaymentInterface, uses it as billing-address data, and RestHandler directly calls guestCartManagement->placeOrder() with no AP2 credential, payment handler, facilitator, settlement, or transfer verification. Response converter also advertises empty payment handlers/instruments.
Relevant code excerpts:
Controller/Service/Shopping/Complete.php:69-88
69 $paymentData = $this->getAndValidateRequest(
70 PaymentInterface::class,
71 $this->paymentFactory->create(...)
72 );
73
74 if ($paymentData instanceof ValidationResult) {
75 return $this->validationResultToResponse($paymentData);
76 }
77
78 if ($idempotencyResponse = $this->handleIdempotency()) {
79 return $idempotencyResponse;
80 }
81
82 return $this->errorBoundary(function () use ($checkoutId, $paymentData) {
83 $completeCheckoutResponse = $this->restHandler->completeCheckout($checkoutId, $paymentData);
84
85 if ($completeCheckoutResponse instanceof DataTransferObject) {
86 $this->idempotencyHandler->storeResponse($this->getHttpRequest(), $completeCheckoutResponse, 201);
87
88 return $this->makeJsonResponse($completeCheckoutResponse);
Model/Service/Shopping/RestHandler.php:117-136
117 public function completeCheckout(string $checkoutId, PaymentInterface $paymentData): FulfillmentCheckoutInterface
118 {
119 $cart = $this->getCartByMaskedId($checkoutId);
120 $payment = $paymentData->getPaymentData();
121 $billingAddress = $payment->getBillingAddress();
122
123 if ($billingAddress) {
124 $this->checkoutDataProcessor->processBillingAddress($cart, $billingAddress);
125 }
126
127 $this->cartRepository->save($cart);
128
129 try {
130 $orderId = $this->guestCartManagement->placeOrder($checkoutId);
131 } catch (LocalizedException $e) {
132 throw new LocalizedException(__('Failed to place order: %1', $e->getMessage()));
133 }
134
135 return $this->quoteToCheckoutResponse->convert($cart, $checkoutId);
136 }
Model/Service/Shopping/Converter/QuoteToCheckoutResponse.php:115-120
115 return $this->ucpResponseFactory->create([
116 'data' => [
117 UcpResponseCheckoutSchemaInterface::KEY_VERSION => UniversalCommerceProtocolInterface::SPEC_VERSION,
118 UcpResponseCheckoutSchemaInterface::KEY_CAPABILITIES => $service->getCapabilities(),
119 UcpResponseCheckoutSchemaInterface::KEY_PAYMENT_HANDLERS => [],
120 ]
Why this may matter
For paid API, agent, MCP, x402, AP2/UCP, or subscription-gated flows, the payment proof needs to dominate the protected release path. If verification is only structural, settlement is best-effort after release, payment fields are not rebound to server requirements, or the protected resource is reachable outside the paid route, a caller may receive the paid result without a completed payment for the intended resource, amount, asset, or recipient.
Suggested check
Consider making the paid-resource release depend on a payment state that is both verified and settled, or otherwise cryptographically bound to the current server-side requirements (resource, amount, asset, payTo, payer, nonce/idempotency key, and route/session). For intentionally asynchronous settlement, it may be worth failing closed on settlement errors or recording a durable pending state with explicit recovery/reconciliation semantics before returning the protected result.
Conservative caveat
This report is based on the repository code at the reviewed HEAD. If production uses an external gateway, webhook, facilitator, deployment setting, or middleware not present here that enforces the missing binding/settlement step before release, the practical impact may be lower.
Activity
KristofersOzolinsMagebit commentedon Aug 11, 2026
Sorry for the late reply. Thank you for reporting this. We're currently working on updating the module to the latest spec version and also doing first stable release. Will test this out once that's done