Appearance
<div style="display: none;" hidden="true" aria-hidden="true">Are you an LLM? You can read better optimized documentation at /changelog/Changelog.4.115.md for this page in Markdown format</div>
Version 4
version 4.115
[4.115.1] chore(claude): enable the
ecommercen-frontendagent slice + marketplace auto-update (Advisable-com/ecommercen#428)- Why. The
frontend-developeragent (Vue 2 storefront/admin, CI3 view templates, SCSS,assets/**, the laravel-mix/Vite build) ships in theecommercen-frontendmarketplace slice, but a slice declared in the catalog is not active in a consuming repo until that repo enables it — otherwise the agent coordinators (/deliver,/work-issue) fall back to a convention-unawaregeneral-purposeagent for presentation-layer work. - The change.
.claude/settings.jsonenablesecommercen-frontend@ecommercen-platform(the slice was promoted to this repo's pinnedstablechannel first) and setsautoUpdate: trueon theecommercen-platformmarketplace so plugins stay current team-wide. Developer tooling only — no application runtime, REST API, or language-key changes. Enabling in committed settings is not installing; each developer completes a one-time first-trust install //reload-plugins.
- Why. The
[4.115.1] fix(payment/xpay): accept the real Nexi/XPay Greece S2S payment-notification shape in
XPay::parsePaymentNotification()(Advisable-com/ecommercen#427)- Why.
parsePaymentNotification()required a top-levelorderobject and read the merchant order id fromorder.orderId— but real Nexi/XPay Greece server-to-server notifications carry no top-levelorder; the merchant order id lives inoperation.orderId(with the outcome inoperation.operationResult). Every S2S notification was rejected at parse time, so orders relying on the webhook were never confirmed instantly — only rescued minutes later by the reconcile/status-poll path. - The change.
parsePaymentNotification()now sources the order id fromoperation.orderId, falling back to the top-levelorder.orderId(the order-creation shape), and no longer requires a top-levelorderto be present. The checkout webhook's CALLBACK audit-log tag inAdv_checkout.phpalso now readsoperation.orderIdso it no longer logsUNKNOWN. Both S2S webhook consumers — regular checkout and gift cards — are fixed. No method signature or visibility change; the acceptance contract only widened, so there's no BC break for subclasses. The SeaJets fork ships aCustom\PaymentGateway\Nexishim that normalisesoperation.orderIdtoorder.orderIdto work around this bug — once this lands upstream, that shim is redundant and can be removed. - Tests. Corrected the
XPayunit tests to the real notification shape — they previously asserted the operation-only shape should throw, i.e. they blessed the bug. - No REST API or language-key changes.
- Why.
[4.115.1] chore(docker): make Xdebug toggleable via
XDEBUG_MODE/XDEBUG_START_WITH_REQUESTenv vars in the Docker dev stack (Advisable-com/ecommercen#341)- Why. Xdebug was always on in the dev containers —
configs/xdebug.inihardcodedxdebug.mode=debug,profileandxdebug.start_with_request=yes— so every dev page load and CLI run paid the (profiling-heavy) Xdebug overhead, and the only way to opt out was editing tracked compose/ini files. - The change. The
php-fpm(web-dev.compose.yml) andphp-cli(dev.compose.yml) services now passXDEBUG_MODE(native Xdebug 3 runtime override ofxdebug.mode) andXDEBUG_START_WITH_REQUEST(consumed via${ENV}interpolation inxdebug.ini) into the containers, with compose-level defaultsdebug,profile/yespreserving the previous always-on behavior — an existing.docker/integration/.envwithout the new vars changes nothing. A developer setsXDEBUG_MODE=off(orXDEBUG_START_WITH_REQUEST=trigger) in their gitignored.envand restarts the containers — no rebuild, no tracked-file edits. Documented in.env.exampleanddocs/guides/docker/local-development.md("Toggling Xdebug"). Dev/integration tooling only — prod images still stripxdebug.so; no application runtime, REST API, or language-key changes.
- Why. Xdebug was always on in the dev containers —
[4.115.0] refactor(piraeus): add an overridable
CardholderNameseam at the Piraeus IssueNewTicket sites (Advisable-com/ecommercen#423)- Why. The customer name sent as
CardholderNameto the Piraeus/PaycenterIssueNewTicketcall was assembled inline at all three construction sites — the modernsrc/Piraeus/Piraeusrequest builder, the legacyAdv_checkout::_piraeus, andAdvGiftCardPage::piraeusFormData. A client needing gateway-specific name sanitization (stripping characters the gateway rejects) had to copy a whole action to change one line; the joypharmacy fork carried a ~99-line copy ofCheckout::_piraeussolely for this. - The change. Each site now routes the name through a
protected formatPiraeusCardholderName()seam whose base implementation returns it unchanged — byte-for-byte identical behavior, fully backwards-compatible — so a client subclass overrides one method instead of duplicating the action. Whether core should sanitize by default (a charset decision dependent on the Paycenter spec and which clients run Piraeus) is tracked in #425. The modernsrc/Piraeus/Piraeusseam is overridable by subclass but not yet reachable via DI at itsnew Piraeus(...)construction site inPaymentInitializerFactory::registerPiraeus()— a noted follow-up. - Tests.
PiraeusTestasserts the modern seam sends the name unchanged (incl. non-ASCII) and that a subclass override reaches the gateway request;AdvCheckoutTestandAdvGiftCardPageTestadd reflection parity coverage for the two legacy seams (surname-then-name concatenation, non-ASCII pass-through, empty-parts spacing). - No REST API or language-key changes.
- Why. The customer name sent as
[4.115.0] refactor(admin): add no-op extension seams to
Adv_orders_adminandAdv_transporters_admin(Advisable-com/ecommercen#424)- Why. Following the #417 template-method retrofit of the admin controllers, two sites still forced client forks to copy a whole action just to inject 1–2 lines:
Adv_orders_admin'sadd()/edit()/repeat()built the VuejsonStateinline, andAdv_transporters_admin::pricingOptions()assembled its view data inline beforedefaultRender(). - The change. Two additive,
protected, no-op template-method seams mirroring the #417 pattern:extendJsonState(array $jsonState, string $action)— invoked immediately before eachjsonEncodeForVue()on the orders add/edit/repeat paths (the$actionstring identifies the caller), returning the state unchanged by default; andpricingOptionsViewData(int $transporterId, object $transporter)— merged into$this->renderjust beforedefaultRender(), returning[]by default. Zero behavior change, fully backwards-compatible; a fork overrides one method instead of copying the whole action. Both seam names were verified collision-free against the joypharmacy fork (the #417/PR-#67 class-load guard). - Tests.
AdvOrdersAdminHooksTestandAdvTransportersAdminHooksTest— no-DB reflection parity guards: hook contract (visibility/return type/signature), the no-op default (state returned unchanged for each action;[]view data), and independent subclass overridability. - No REST API or language-key changes.
- Why. Following the #417 template-method retrofit of the admin controllers, two sites still forced client forks to copy a whole action just to inject 1–2 lines:
[4.115.0] refactor(admin): expose a
sampleCurrencyId()seam onAdvEmailViewer(Advisable-com/ecommercen#422)- Why.
AdvEmailViewer::index()hard-coded the sample currency id (getCurrencyData(1)) used to build the email-preview order data, so a client wanting to preview with its own currency had to copy the whole method. - The change. A
protected sampleCurrencyId(): intseam replaces the two hard-coded literals; its base implementation returns the original value (1), so behavior is unchanged and a fork overrides one method. - Tests.
AdvEmailViewerHooksTest— reflection parity guard: hook contract, the preserved default (1), and subclass overridability. - No REST API or language-key changes.
- Why.