Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
017756a
Add magebit:seed:ucp-conformance, seeding the fixed products, the out…
KristofersOzolinsMagebit Aug 11, 2026
3ab80e2
Require PHP 8.2, matching the spec libraries and Magento 2.4.7.
KristofersOzolinsMagebit Aug 12, 2026
0708979
Adopt UCP 2026-04-08: own the total-type vocabulary the spec opened u…
KristofersOzolinsMagebit Aug 12, 2026
f7d33fc
Point the totals-type DI map at the module's own constants; the spec'…
KristofersOzolinsMagebit Aug 12, 2026
be49f6a
Adopt the shared money, checkout-state and buyer primitives from Mage…
KristofersOzolinsMagebit Aug 12, 2026
3c497dc
Move UCP idempotency onto the shared coordinator, leaving only the ma…
KristofersOzolinsMagebit Aug 12, 2026
0112a7e
Move both routers onto the shared pattern router. ACP's session actio…
KristofersOzolinsMagebit Aug 12, 2026
427256f
Move UCP onto the shared line item writer, address writer, shipping m…
KristofersOzolinsMagebit Aug 12, 2026
126ac62
Add the shared session-to-order link and move both modules onto it. A…
KristofersOzolinsMagebit Aug 12, 2026
90e034d
Advertise the spec version the module actually speaks. It declared 20…
KristofersOzolinsMagebit Aug 12, 2026
13b4e69
Encrypt UCP's stored idempotency response bodies, matching the other …
KristofersOzolinsMagebit Aug 12, 2026
67b8fc8
Persist the agent's own webhook URL with the checkout. The profile pa…
KristofersOzolinsMagebit Aug 12, 2026
4f35705
Build UCP's order-event webhook transport: Content-Digest per RFC 953…
KristofersOzolinsMagebit Aug 12, 2026
aa37abf
Emit discount totals negative, as 2026-04-08 requires. The pinned tar…
KristofersOzolinsMagebit Aug 12, 2026
2d0dfb1
Send the spec's error_response envelope instead of an invented one. 2…
KristofersOzolinsMagebit Aug 12, 2026
eadd775
Serve the Order capability: GET /ucp/shopping/orders/{id} returns the…
KristofersOzolinsMagebit Aug 12, 2026
1b3339c
Point selected_destination_id at a destination the response lists, an…
KristofersOzolinsMagebit Aug 12, 2026
59022e1
Send order lifecycle events, which the transport was built for but ha…
KristofersOzolinsMagebit Aug 12, 2026
5f7ebde
feat: advertise dev.ucp.shopping.fulfillment as a checkout extension
KristofersOzolinsMagebit Aug 13, 2026
d0afcfd
feat: sign order-event webhooks per RFC 9421 and publish the key in d…
KristofersOzolinsMagebit Aug 13, 2026
21339c2
feat: serve the cart capability as its own resource, with a guard for…
KristofersOzolinsMagebit Aug 13, 2026
bc480bb
feat: serve catalog search, lookup and product reads
KristofersOzolinsMagebit Aug 13, 2026
1d6b893
feat: record and label orders placed over UCP
KristofersOzolinsMagebit Aug 31, 2026
b2fb851
feat: add secret-gated shipping simulation endpoint for conformance t…
KristofersOzolinsMagebit Aug 31, 2026
ae7a2d6
fix: pass free-form spec maps through the request builder and report …
KristofersOzolinsMagebit Aug 31, 2026
9f7b885
fix: never report a fulfillment option the store does not offer
KristofersOzolinsMagebit Aug 31, 2026
e9e70f4
fix: answer refused checkouts with typed messages an agent can act on
KristofersOzolinsMagebit Aug 31, 2026
aaccb64
feat: serve order updates so an agent can ask for a refund or return
KristofersOzolinsMagebit Aug 31, 2026
0ac9dce
feat: serve buyer consent, and answer with the shape each response ac…
KristofersOzolinsMagebit Aug 31, 2026
5c46898
feat: let a deployment name profile origins the SSRF guard would refu…
KristofersOzolinsMagebit Aug 31, 2026
336bc10
fix: bill where the order ships, and report a fulfillment method with…
KristofersOzolinsMagebit Aug 31, 2026
3ee59cd
feat: warn when a request carries an extension the store does not imp…
KristofersOzolinsMagebit Sep 1, 2026
d1af307
refactor: take the request validator and hydrator from the shared base
KristofersOzolinsMagebit Sep 9, 2026
63f69ac
refactor: take the stock check, buyer writer and total label from the…
KristofersOzolinsMagebit Sep 9, 2026
f9e97ef
refactor: take idempotency arbitration from the shared gate
KristofersOzolinsMagebit Sep 9, 2026
e3badec
refactor: report the shared readiness findings, and catch a street th…
KristofersOzolinsMagebit Sep 9, 2026
ba21f0a
refactor: run the shared webhook dispatch behind this module's toggle
KristofersOzolinsMagebit Sep 9, 2026
07aada5
refactor: take the JSON endpoint plumbing from the shared base
KristofersOzolinsMagebit Sep 9, 2026
72b614d
build: require the spec library release that carries the schema's val…
KristofersOzolinsMagebit Sep 9, 2026
48cd93e
fix: put the agent webhook URL through the SSRF guard before storing it
KristofersOzolinsMagebit Sep 9, 2026
47b0366
perf: page catalog search in the query instead of loading every match…
KristofersOzolinsMagebit Sep 9, 2026
e217293
fix: cap catalog lookup, read its SKUs in one query, and hide product…
KristofersOzolinsMagebit Sep 9, 2026
8d470af
test: pin the catalog handler's lookup cap, paging and storefront vis…
KristofersOzolinsMagebit Sep 9, 2026
16e0ad4
fix: address orders by their checkout session so order numbers cannot…
KristofersOzolinsMagebit Sep 9, 2026
2f2638b
perf: load an order's product images in one query instead of one per …
KristofersOzolinsMagebit Sep 9, 2026
dd6b371
ci: run phpstan and the unit tests against a real Magento install on …
KristofersOzolinsMagebit Sep 9, 2026
f54cd23
test: find the spec schemas in the installed package, not in this che…
KristofersOzolinsMagebit Sep 9, 2026
20417c7
ci: install the schema validator and point phpstan at the generated f…
KristofersOzolinsMagebit Sep 9, 2026
9fac353
refactor: import the spec discovery types by their real names, not al…
KristofersOzolinsMagebit Sep 9, 2026
387baf6
fix: page catalog search by distinct product id so a page holds whole…
KristofersOzolinsMagebit Sep 9, 2026
cb4d15c
test: cover that orders are addressed by checkout session, not by ord…
KristofersOzolinsMagebit Sep 9, 2026
f6d00dc
perf: load a catalog page's variants, stock and urls in one query each
KristofersOzolinsMagebit Sep 10, 2026
175bc52
fix: read a page's stock without a base module release the modules ca…
KristofersOzolinsMagebit Sep 10, 2026
05365c5
fix: alias the variant collection namespace so phpstan does not inven…
KristofersOzolinsMagebit Sep 10, 2026
21f34a9
fix: group variants from the link table so a shared variant does not …
KristofersOzolinsMagebit Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: CI

on:
push:
branches: [main, master, develop]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- name: Checkout module
uses: actions/checkout@v4

- name: Allow phpstan/extension-installer composer plugin
run: composer config --global allow-plugins.phpstan/extension-installer true

# A module repository has no Magento to analyse against, so one is built around it.
- name: Set up Magento project with module
id: magento
uses: magebitcom/setup-magento-module-action@v1
with:
magento-version: "2.4.7-p3"
php-version: "8.3"
composer-auth-user: ${{ secrets.MAGENTO_AUTH_USER }}
composer-auth-pass: ${{ secrets.MAGENTO_AUTH_PASS }}
extra-packages: >-
phpstan/phpstan:^2.0
bitexpert/phpstan-magento:^0.42
phpstan/extension-installer:^1.4
phpunit/phpunit:^9.5
opis/json-schema:^2.6

# bitexpert/phpstan-magento only invents a factory class when Composer cannot find a real
# one, and that fallback is broken on newer phpstan/phpstan. Compiling writes the real
# factories to disk, so the fallback never runs.
- name: Generate DI factories for PHPStan
working-directory: ${{ steps.magento.outputs.project-path }}
run: |
php bin/magento module:enable --all
php -d memory_limit=-1 bin/magento setup:di:compile

# The factories compiled above are loaded by a Magento autoloader at runtime, not through
# Composer, so PHPStan has to be pointed at them. Left unfound, it invents its own and trips
# over the broken fallback described above.
- name: Run PHPStan
working-directory: ${{ steps.magento.outputs.project-path }}
run: |
cat > phpstan-ci.neon <<'NEON'
includes:
- ${{ steps.magento.outputs.module-path }}/phpstan.neon
parameters:
scanDirectories:
- generated/code
NEON
vendor/bin/phpstan analyse "${{ steps.magento.outputs.module-path }}" \
-c phpstan-ci.neon --no-progress --memory-limit=4G

unit-tests:
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout module
uses: actions/checkout@v4

- name: Set up Magento project with module
id: magento
uses: magebitcom/setup-magento-module-action@v1
with:
magento-version: "2.4.7-p3"
php-version: "8.3"
composer-auth-user: ${{ secrets.MAGENTO_AUTH_USER }}
composer-auth-pass: ${{ secrets.MAGENTO_AUTH_PASS }}
extra-packages: >-
phpunit/phpunit:^9.5
opis/json-schema:^2.6

# Extension attribute interfaces such as CartExtensionInterface are generated, and tests that
# mock one need the real thing rather than the empty stub that ships in vendor.
- name: Generate the extension attribute classes
working-directory: ${{ steps.magento.outputs.project-path }}
run: |
php bin/magento module:enable --all
php -d memory_limit=-1 bin/magento setup:di:compile

- name: Run unit tests
working-directory: ${{ steps.magento.outputs.project-path }}
run: >-
vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist
"${{ steps.magento.outputs.module-path }}/Test/Unit" --testdox
14 changes: 14 additions & 0 deletions Api/Data/CheckoutMetaInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface CheckoutMetaInterface
public const ORDER_ID = 'order_id';
public const WEBHOOK_URL = 'webhook_url';
public const SUBMITTED_FULFILLMENT = 'submitted_fulfillment';
public const BUYER_CONSENT = 'buyer_consent';
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';

Expand Down Expand Up @@ -105,6 +106,19 @@ public function getSubmittedFulfillment(): ?string;
*/
public function setSubmittedFulfillment(?string $submittedFulfillment): self;

/**
* The data-processing consent the agent recorded for this buyer, as JSON.
*
* @return string|null
*/
public function getBuyerConsent(): ?string;

/**
* @param string|null $buyerConsent
* @return $this
*/
public function setBuyerConsent(?string $buyerConsent): self;

/**
* Get created at
*
Expand Down
125 changes: 0 additions & 125 deletions Api/Data/IdempotencyKeyInterface.php

This file was deleted.

76 changes: 76 additions & 0 deletions Api/Data/OrderAdjustmentInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/**
* This file is part of the Magebit_UniversalCommerce package.
*
* @copyright Copyright (c) 2026 Magebit, Ltd. (https://magebit.com/)
* @author Magebit <info@magebit.com>
* @license MIT
*/

declare(strict_types=1);

namespace Magebit\UniversalCommerce\Api\Data;

/**
* One post-order adjustment an agent asked this store to record against an order.
*/
interface OrderAdjustmentInterface
{
public const ENTITY_ID = 'entity_id';
public const ORDER_ID = 'order_id';
public const ADJUSTMENT_ID = 'adjustment_id';
public const PAYLOAD = 'payload';
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';

/**
* @return int|null
*/
public function getEntityId(): ?int;

/**
* @return int|null
*/
public function getOrderId(): ?int;

/**
* @param int $orderId
* @return $this
*/
public function setOrderId(int $orderId): self;

/**
* @return string|null
*/
public function getAdjustmentId(): ?string;

/**
* @param string $adjustmentId
* @return $this
*/
public function setAdjustmentId(string $adjustmentId): self;

/**
* The adjustment as the agent submitted it, as JSON.
*
* @return string|null
*/
public function getPayload(): ?string;

/**
* @param string $payload
* @return $this
*/
public function setPayload(string $payload): self;

/**
* @return string|null
*/
public function getCreatedAt(): ?string;

/**
* @return string|null
*/
public function getUpdatedAt(): ?string;
}
30 changes: 30 additions & 0 deletions Api/Data/TotalTypeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/**
* This file is part of the Magebit_UniversalCommerce package.
*
* @copyright Copyright (c) 2025 Magebit, Ltd. (https://magebit.com/)
* @author Magebit <info@magebit.com>
* @license MIT
*/

declare(strict_types=1);

namespace Magebit\UniversalCommerce\Api\Data;

/**
* The total types this module emits. UCP `2026-04-08` made `total.type` an open string — it documents
* these as well-known values in prose and lets businesses add their own — so the vocabulary is no
* longer generated from the schema and belongs to whoever writes the totals.
*/
interface TotalTypeInterface
{
public const TYPE_SUBTOTAL = 'subtotal';
public const TYPE_ITEMS_DISCOUNT = 'items_discount';
public const TYPE_ITEMS_BASE_AMOUNT = 'items_base_amount';
public const TYPE_DISCOUNT = 'discount';
public const TYPE_FULFILLMENT = 'fulfillment';
public const TYPE_TAX = 'tax';
public const TYPE_FEE = 'fee';
public const TYPE_TOTAL = 'total';
}
Loading
Loading