Magento module
The Sarvex UCP Magento module (Sarvex_Ucp) integrates your Magento 2 store with Sarvex's Google UCP checkout platform. It mirrors plugin/woocommerce's scope exactly (see CLAUDE.md §4 — "the plugin…
Generated from plugin/magento/README.md in the Sarvex repository — edit the source there, not this page.
The Sarvex UCP Magento module (Sarvex_Ucp) integrates your Magento 2 store with Sarvex’s Google UCP checkout platform. It mirrors plugin/woocommerce’s scope exactly (see CLAUDE.md §4 — “the plugin has two roles, keep it thin”):
- Signal Sending — Notifies Sarvex when products or inventory change
- Profile Proxy — Serves the UCP profile at
/.well-known/ucp - Pairing — Securely pairs your store with Sarvex
No business logic lives here — no scoring, mapping, or UCP interpretation. Everything that smells like logic belongs server-side, in Sarvex’s sync plane.
Installation
Option A: Composer (recommended)
composer require sarvex/module-ucp-gateway
bin/magento module:enable Sarvex_Ucp
bin/magento setup:upgrade
bin/magento cache:flush
Option B: Manual (app/code)
- Copy this directory to
app/code/Sarvex/Ucp/in your Magento installation - Run:
bin/magento module:enable Sarvex_Ucp bin/magento setup:upgrade bin/magento setup:di:compile bin/magento cache:flush
Configuration
After installation, go to Stores → Configuration → Services → Sarvex UCP Gateway.
Required Settings
Sarvex Endpoint URL
- What it is: The URL of your Sarvex backend server
- Format:
https://sarvex.example.com(no trailing slash) - Required: Yes, before pairing
Store ID / Connector ID / HMAC Secret
- Populated automatically by the pairing flow (see below)
- Read-only; the HMAC secret is stored encrypted via Magento’s own config encryption (
Magento\Config\Model\Config\Backend\Encrypted)
How Pairing Works
Pairing is a two-step handshake, identical to the WooCommerce plugin’s:
- Begin pairing —
Sarvex\Ucp\Model\PairingManager::beginPairing()calls Sarvex with a bearer token (from the Sarvex console/operator flow) and receives a one-time pairing code + connector ID. - Complete pairing —
PairingManager::completePairing()exchanges that pairing code for durable connector credentials, HMAC-signing the request with the pairing code itself as the one-time secret. The pairing code then becomes the permanent HMAC secret for all future signed requests.
This module exposes the PairingManager service; wiring a specific admin action/button to drive it end-to-end (equivalent to the console-triggered flow the operator UI uses) is left to the Sarvex console integration, matching how the WooCommerce plugin also only exposes the read-only result of an externally-driven pairing.
How It Works
Product and Inventory Signals
Whenever a product save or a stock item save commits (bound to catalog_product_save_commit_after / cataloginventory_stock_item_save_commit_after, not the _after variants, so the signal never races Sarvex’s pull against an uncommitted transaction), the module sends a lean signal to Sarvex:
{"sku": "...", "entity_kind": "product"}
Sarvex pulls the full product/inventory details from your store in response — the signal contains only the SKU and entity kind, never product data, PII, or payment credentials (thin events, fat pull, per CLAUDE.md §4).
Profile Serving
/.well-known/ucp is served by a dedicated router (Sarvex\Ucp\Routing\WellKnownRouter, registered ahead of Magento’s standard router — see etc/frontend/di.xml) and controller (Sarvex\Ucp\Controller\Wellknown\Ucp):
- Fetches the profile live from Sarvex (
GET /connector/profile, HMAC-signed) on every request - Falls back to the last-known-good cached copy if Sarvex is unreachable
- Always returns
200with Sarvex’s bytes as-is — never a redirect - Holds no private keys and authors nothing
Webserver-level fallback (optional hardening)
The custom router is self-contained and needs no webserver configuration. If your deployment’s edge proxy or CDN intercepts .well-known/* paths before they reach Magento’s index.php (common with some ACME/Let’s Encrypt setups), add an explicit passthrough rewrite as a fallback, e.g. for nginx:
location = /.well-known/ucp {
rewrite ^ /index.php$request_uri last;
}
Security Considerations
- HMAC Signing — All requests to Sarvex are signed with HMAC-SHA256 (
Sarvex\Ucp\Model\Hmac), byte-identical to the WooCommerce plugin’s scheme, so Sarvex’s signature verification is platform-agnostic. - Credential Storage — Store ID, Connector ID, and the endpoint URL are stored in
core_config_data; the HMAC secret is stored encrypted via Magento’s config encryption. - Sequence counters — Persisted in
core_config_data(not the object cache), so an admincache:flushcannot reset them and cause Sarvex’s inbound sequence-based de-dup (CLAUDE.md invariant 6) to treat a genuinely new signal as an already-seen retry. - Data in Signals — Product/inventory signals contain only the SKU and entity type. No payment credentials or PII.
Module Structure
plugin/magento/
├── registration.php # Module registration
├── composer.json
├── etc/
│ ├── module.xml
│ ├── config.xml # Default config values
│ ├── acl.xml # Admin ACL resource
│ ├── events.xml # Observer bindings
│ ├── adminhtml/system.xml # Stores > Configuration UI
│ └── frontend/
│ ├── routes.xml # frontName for the Wellknown controller's resolution
│ └── di.xml # Registers WellKnownRouter
├── Model/
│ ├── Config.php # Store-scoped config read/write (get_option/update_option equivalent)
│ ├── Hmac.php # HMAC-SHA256 signing (matches WooCommerce's HMAC.php)
│ ├── SignalSender.php # Product/inventory change signals
│ ├── PairingManager.php # Pairing handshake
│ ├── ProfileClient.php # Fetches the live profile from Sarvex
│ └── ProfileCache.php # Last-known-good profile fallback cache
├── Observer/
│ ├── ProductSaveObserver.php # catalog_product_save_commit_after
│ └── StockItemSaveObserver.php # cataloginventory_stock_item_save_commit_after
├── Routing/
│ └── WellKnownRouter.php # Matches literal /.well-known/ucp
├── Controller/Wellknown/
│ └── Ucp.php # Serves the profile
├── Test/Unit/Model/
│ ├── HmacTest.php
│ └── PairingManagerTest.php
└── README.md # This file
Development
Running Tests
Test/Unit/Model/HmacTest.php has no Magento dependency and runs with a bare PHPUnit install:
cd plugin/magento
composer require --dev phpunit/phpunit
vendor/bin/phpunit Test/Unit/Model/HmacTest.php
The rest of the suite (PairingManagerTest.php, and any future observer/controller tests) mocks Magento\Framework\HTTP\Client\Curl and other framework classes, so it requires a Magento development environment (magento/framework installed via composer require sarvex/module-ucp-gateway inside a real Magento project, or Magento’s own component test harness) to execute — the same way the WooCommerce plugin’s test suite requires a WordPress test harness (WP_Mock/Brain Monkey) not bundled in this repo.
Note on verification: this module is written to Magento 2’s documented module/router/observer/config conventions, but — unlike the WooCommerce plugin, which is exercised end-to-end by
internal/planes/sync/simandtest/e2e/journey— there is no Magento instance orinternal/planes/sync/sim-equivalent fake backend in this repository to run it against yet. Smoke-test the/.well-known/ucprouting and the pairing/signal flow against a real (or fake) Magento instance before relying on it in production. Building that fake-backend simulator is tracked as a follow-up (see the Magento completion plan’s Phase 3).
Support
For issues or questions, check the module’s logs via Magento’s standard var/log/system.log / var/log/exception.log (all log lines are tagged sarvex_ucp:), then contact Sarvex support with the relevant excerpt (sanitize the HMAC secret before sharing).
License
This module is part of Sarvex and is licensed under the Apache License 2.0.