Skip to content
Skip to content
Documentation navigation

Ecommerce

Ecommerce provides a tenant's storefront, catalog, customer accounts and sales backoffice. Cart owns shopping persistence and lifecycle; Ecommerce supplies retail pricing and verified customer ownership. Page owns public URL rewrites, Media supplies images, and configured Payment and Invoice capabilities retain their own responsibilities.

This guide describes the current 0.36.0 source. Availability depends on active modules, migrations, permissions, tenant configuration and deployed theme assets. Source tests do not establish a shop's provider readiness, supported countries, fiscal completeness or privacy policy. Inline checkout address entry and native postal review are implemented; country-sensitive fiscal data remains unfinished.

Capability Current scope
Catalog, retail pricing, native carts and session-bound checkout Implemented; tenant migrations, assets and provider checks still apply
Customer access and staff administration Implemented with the credential/account limits described below
Invoice, connector imports and external catalog indexes Optional active/configured owners; not provided by a theme alone
Inline guest/registered delivery/billing and complete native postal review Implemented with bounded drafts, owned saved/order-only edit choices and required fresh shipment delivery
Browser cache adoption, country/fiscal completion and consent/erasure qualification Unfinished; do not infer support from the implemented checkout
Public merchant feeds, automatic recovery campaigns and unqualified countries/regimes No delivered contract enabling these workflows

Enable a shop

Enable ecommerce and its cart, page and media dependencies for the selected tenant. Public Front and the selected theme supply the surrounding website. Themes and feature modules remain independent: a theme without a specialized shop template uses the canonical module fallback.

Back up the tenant database, apply the release's migrations, configure catalog and commercial settings, and check the deployed theme assets before accepting orders. Full migration handles active module dependencies; a module-filtered run covers only its selected migration scope. The owned MySQL 8.4.11 complete fresh command and unchanged rerun now pass after the owner identity/index and bootstrap repairs. This qualifies the isolated canonical fresh migration, not existing-tenant full upgrades, checkout/lifecycle/ concurrency, native PostgreSQL/MariaDB or deployment. Existing owner identity upgrades require quiesced writers and reviewed backups; see the migration reference.

TENANT_ENV=k0smos.example.com php bin/console migrate --allow-no-migration
TENANT_ENV=k0smos.example.com php bin/console tenant:theme:diagnose

These are deployment commands, not a request to run them on an existing shop. See tenant configuration, frontend delivery and the command inventory.

Public route examples below show unprefixed paths. Use the shop's generated language links: prefixes follow the tenant's locale policy, and may produce paths such as /en/shop/login. Staff routes remain under /admin.

Browse the catalog

Open /shop, a category at /shop/categories/{slug}, or a product at /shop/products/{slug}. Products include descriptions, specifications, images, availability and, when configured, variants and related products. Select required variant options before adding a quantity. Variant-specific image galleries use the product gallery when none is curated separately.

Search and list controls support text, category, pagination, available ordering and price ranges. A price filter compares the catalog's base currency, shown beside the control; the displayed shopping currency can differ. Category facet counts represent the current query's primary-category buckets, not every descendant or additional membership. LTS1's unfiltered category landing counts have a separate contract and disappear on text/price-filtered lists.

SQL supplies canonical catalog reads and the conservative search fallback. Configured OpenSearch/Elasticsearch, Meilisearch or Typesense indexes are disposable projections with declared filter/facet capabilities. Predictive search is bounded and rate-limited. An index outage does not authorize exposing inactive products or another tenant's data. Index hits select product identities; current public SQL supplies response fields. Arbitrary provider fields, private URLs and withdrawn snapshots are omitted. Totals/facets remain provider estimates after fresh hydration. See search and database configuration.

Operators maintain products, categories and variants under /admin/catalog/products and /admin/catalog/categories. Localization tabs show translated fields and source provenance. Incomplete translations fall back to the configured default-locale content. A copied translation is an explicit operator action.

Published menu entries and category blocks can compose a desktop Mega Menu and a native mobile category tree. Category reads are bounded; inactive or stale references are omitted publicly. New module menu offers default to hidden, and stored visibility/order remain operator-controlled. LTS1 presents Catalog and account/cart utilities through its own navigation composition.

Imported product/category URLs can retain their source canonical when the Page rewrite feature is configured. Legacy numeric product URLs redirect to their canonical destination. Removed imported URLs can remain HTTP 410. An external search index does not own these URLs.

Product/category pages publish supplied canonical links and JSON-LD through all eight storefront layouts, including CC1. Supplied robots directives reach each head; staff previews explicitly carry noindex, nofollow. Existing page URL policy and published prices/availability remain authoritative. Rendering and isolated server checks cover this metadata boundary; they do not establish deployed search indexing or rich-result eligibility.

Ordinary catalog/category pages use a canonical for each default-sort result page: the base URL for page one and ?page=N afterwards. Numeric category links use the same page of the enabled source or native category URL. Category redirects retain normalized search, sort, price and page selections. Internal search and alternative sort/price listings remain usable with noindex, follow and no canonical/collection schema. An empty valid first page also uses noindex; unknown/inactive numeric categories and pages beyond their result count return 404. Explicit empty metadata overrides theme fallback, while absent values keep the existing fallback. Native pagination preserves source paths and selection.

The public sitemap uses /sitemap.xml as an XML index and /sitemaps/catalog/{locale}/{page}.xml for bounded catalog slices. Each reads at most 1,000 active scalar product/category identities, with real translations for additional configured languages. Enabled source canonicals replace native URLs after public route and slug-owner checks; private/conflicting URLs are excluded. Empty or withdrawn slices return 404. Counts describe current candidate slices, so an index reference can become unavailable after filtering or publication changes. Configuring a language does not create its native routes.

Page/Blog emit known valid modification dates; catalog dates remain absent until variants, inventory and collection updates share a complete page clock. Generation time and rewrite timestamps are never published as content dates. The index has no fabricated modification date. Source qualification covers pagination beyond the search offset ceiling, canonical/locale ownership, private URL exclusion and isolated HTTP rendering. Actual robots/CDN/bot rules, large-catalog SQL performance and deployed indexing remain to qualify.

Product details now publish reciprocal hreflang only when exact visible translations and public same-tenant native/source identities qualify. Missing, metadata-only, empty, private or shadowed translations publish none; language navigation may still offer fallback content. All eight public heads use this qualified metadata, including a self member and x-default only when the default language qualifies. Unsupported locale inventories preserve content and omit optional alternates. Blog uses the same transport with its own publication and whole-row overlay rules. Indexable catalog/category pages now share the same bounded SQL inventory and base-name/ID order across languages. Every rendered product needs an explicit translated name; secondary fields retain fallback. Categories need qualified native/source identity. Incomplete translations withdraw the affected slice; complete other pages remain eligible. A slugless category can qualify through a valid source canonical; numeric access alone stays unqualified. Configured search/sort/price providers retain noindex views. Source prefixes reach layouts/widgets on first visits and override conflicting sessions. Owned SQL/Kernel checks prove actual page-two/three inventory; synthetic heads prove transport.

A positive ?variant=ID preselects only a variant belonging to the product, including a sold-out variant. Invalid or foreign IDs fall back to the first available variant, then the first variant if all are unavailable. Numeric, retired-slug and native-to-source catalog redirects preserve valid selection; manual Page redirects retain their configured destination policy. Product canonical/hreflang identify the query-free family. Source/preview no-JS selectors, language navigation and currency returns preserve the chosen variant. Inline selection updates keep visible SKU, converted price/currency, stock and images aligned. Plain Product graphs update Offer URLs and clear previous schema images for empty galleries; complete ProductGroup graphs stay unchanged. These source/temporary-server checks do not establish deployed acceptance.

Variant names now share requested/default/base translation fallback between detail and search. Products without variants retain Product/Offer; variant families publish a complete ProductGroup with a separate Product/Offer and direct selection URL for each variant. Prices, currency, stock and safe images use actual catalog data. Saved scalar attributes retain their names, including zero/false values; only differing values describe generic variation axes. Family reviews are not attributed to individual variants. Client selection retains the complete group graph. Unknown brands, GTIN and merchant policies are omitted.

Merchant-policy markup, rich-result eligibility and deployed content, mobile performance and accessibility parity remain to qualify. Generative Engine Optimization concerns factual public content; geolocation tracking and ranking or AI-citation guarantees are not included.

Inventory, prices and commercial settings

The inventory workspace at /admin/ecommerce/inventory manages locations, sellable products/variants, quantities, thresholds and append-only movements. Adjustments check row versions and record retry receipts. Sellable availability also considers current reservations. Accepted checkout holds are retained for verified payment completion or failure; ordinary expiry cannot release them.

VAT rates, currencies and shipping methods have separate administration pages. Rates are configured manually; there is no automatic exchange-rate feed. Retail checkout supports active currencies with two decimal places and rejects invalid rates, unsupported precision and overflow. Variant prices are absolute amounts in their parent currency. Monetary totals from different denominations remain separate.

Checkout recalculates tax-inclusive unit amounts, shipping, coupons and promotions through one authority. Coupon discounts apply before promotions; fixed discounts, thresholds and shipping use base-currency configuration. An invalid coupon remains editable and does not silently become an accepted discount. Coupon usage is claimed at acceptance rather than during browsing. Country-sensitive taxation and invoice treatment require the shop's separately qualified market configuration.

Operators cannot delete a VAT rate used by products, a shipping method used by orders or the base currency. Changing the base currency preserves configured exchange-rate ratios; it does not rewrite historical accepted orders.

Shopping carts and account handoff

The canonical /cart page restores the verified SQL cart. Registered customers use their current account ownership; guests use tenant/session-bound credentials in an HttpOnly cookie. A contact email, staff identity or supplied token does not establish cart ownership.

Native product/cart forms support add, quantity changes, remove, explicit clear, coupon changes and checkout without JavaScript. The server validates products, variants, quantities, prices and stock. Managed mutations use current versions, CSRF/origin checks and retry receipts. Conflicting versions require a refreshed selection and shopper review. Polling and exact replay do not renew inactivity.

On successful customer login, registration or password-reset sign-in, verified guest selections can transfer into the account cart. Authentication remains successful if cart transfer needs attention. The shop links to /shop/cart/merge for native quantity, currency and coupon resolution; rejected choices preserve the selections and pending recovery. Source credentials are never placed in the form. Logout detaches session linkage while retaining the account's SQL cart.

LTS1 header, cart sheet and catalog quick-add restore SQL into memory through the authorized reader. Explicit writes preserve revisions and exact retry receipts; identity changes fence old replies. Loading/failure states are visible. This source requires the maintainer's LTS1 JavaScript build.

Shared cart consumers clear private rows and reviews when the document becomes hidden or leaves the page. They make no new background cart request and restore only after fresh authorized SQL on return. An uncertain already-sent action keeps its explicit exact retry until current identity is verified; it is never resubmitted automatically. Actual browser/BFCache and deployed identity journeys remain to qualify with the maintainer-built bundles.

Successful login, signup, password reset and logout also invalidate other visible compiled cart readers, including after native redirects to unrelated pages and JSON migrated-password notices. A host-only Secure cookie contains only a random invalidation nonce and expires after 60 seconds. Visible readers use cookie events and a one-second local check, without idle network polling; they clear old presentation before reading current authorized SQL. Failures/reset-link requests emit no signal and notifications never submit/retry shopping commands. HTTPS and maintainer-built bundles are required; actual concurrent PHP sessions, devices and deployed journeys remain separately qualified.

Isolated real-HTTP checks now cover concurrent login/cart handoff, two guest merges into one account, native login/logout/account switching and bounded response/session-loss recovery on temporary SQLite. The 25 cases include pending native review, stale versions, controlled source mutation after capture and receipt recovery after a native session write fails, including a newer reviewed source. Dedicated authenticated completion recovers only an already accepted receipt and never adds or reprices the selection again. Quantities and once-only receipts remain correct, with unchanged financial/ inventory records. Losing the authenticated session preserves SQL selections but cannot reconstruct its pending guest grant. Authentication and explicit merge persist native session changes before success; refused persistence returns a private failure/native review while accepted SQL remains recoverable. Ten cases execute guarded production PHP entrypoint code, including session startup, regeneration and write refusals. Two concurrent guest requests after capture now verify fresh review or stale refusal without a second transfer, using exact native file-lock waits on Linux. Other session backends, remote databases, deployed assets and representative devices still require work or qualification.

The same25-case HTTP matrix can also run against runner-owned MySQL/PostgreSQL, with real engine-observed SQL waits, native FK/data checks and unchanged schema. This extends temporary native SQL coverage; deployed HTTPS/browser delivery, other session backends and real provider acceptance remain separate gates.

Wishlist cart adds across all eight public themes also restore authorized SQL through the shared compiled model. It verifies the page's identity generation, preserves saved wishes and keeps exact retries with the initiating row. All eight front bundles require maintainer compilation before rollout.

Canonical catalog controls now load the shared SQL reader when the page starts. Quick-add sends one product unit; controls wait for verified state and show loading or failure. An uncertain add keeps its exact retry at the original product before another product can be added. After a definitive server replay refusal, choose “Check current quantities” to review SQL, then “Continue with these quantities” to verify them again and stop that browser retry. An earlier committed addition is preserved. Changed/failed reads require another review; transport, CSRF and rate-limit failures retain exact retry. New additions require separate actions. Native detail links remain usable without JavaScript, and a fresh cart visit restores current server state. Personalized wishlist hints appear only after matching the page generation; authenticated catalog HTML is private and cannot be stored by shared caches.

Dormant product cart writers and unmounted inline cart/sync factories are removed; product and cart purchases retain native SQL forms. Payment results refresh current authorized projections with a content-free signal; legacy selections are neither read nor removed. One-time legacy selection import now has a strict server protocol: explicit candidate quantities are validated and repriced into current authorized SQL, while a global hashed claim prevents repeat imports. Historical tokens grant no read or ownership. Only existing live unmanaged/unowned/unsettled references qualify; unknown or terminal references refuse. Cart migration Version20261012150000 opens one nonrenewable 30-day cutover window. On the cart page, choose “Review saved selections” to explicitly check old browser storage. The review shows unvalidated product/variant references and quantities separately from current server quantities; cached names, prices and contact data are ignored. “Confirm import” adds those quantities only after another unchanged server read and server product/stock/price validation. Changed cart/account/storage requires another review. An unknown outcome retains “Retry this import” for its exact original attempt; definitive refused replay also offers current-quantity review and explicit disposition, preserving any SQL effects. Absent/empty, unreadable, denied or unsynced-only storage and unknown/terminal/ already-imported/closed-window references have visible feedback and current-cart/ catalog links. Only confirmed acceptance attempts to remove unchanged originating cart/token keys; newer/unrelated/revision data is preserved. Cleanup is best effort against old concurrent browser writers, and a confirmed result with retained local data is labelled separately. Open the updated native cart before continuing. Reviewed tenant cutover and owner-built journeys remain required; no automatic import is shipped. Unmanaged API retirement and full concurrent login/old-tab/device qualification remain open. Wishlist refresh before a toggle is a client check; server generation assertions, CSRF and inactive-customer write policy still require qualification. All eight front bundles require maintainer compilation before these source changes roll out.

Legacy POST|DELETE /shop/cart/coupon is retired, including locale aliases. Both actions return private HTTP 410 cart.legacy_coupon_retired, with /cart, /cart/bootstrap and /cart/mutate recovery paths. They never process tokens, read shopping, set/remove saved coupons or consume usage. Use the native cart coupon form or protected checkout preview; strict API consumers use explicit coupon intent after authorized bootstrap. Deploy retirement together with maintainer-built migrated bundles at reviewed cutover. POST /cart/sync also returns private 410 cart.legacy_sync_retired without reading inputs or SQL; its fixed recovery paths are the same. Token-based checkout shopping/fresh settlement is also retired. Every read uses verified SQL authority and fresh acceptance requires a current shopping grant. Accepted session-bound receipt replay/resume remains supported after shopping credentials close or a gateway changes; a historical token alone grants no shopping access.

Review activity and retention

Staff with ecommerce.view open /admin/carts. The default selects nonempty, unexpired idle shopping carts without accepted checkout evidence. Filters cover account/guest, inactivity, lifecycle, shopping expiry and stored-email presence. SQL pagination returns 50 carts; full quantities and totals cover all matches, separately by currency. Rows show numeric cart IDs, inactivity age, expiry and bounded product previews.

Only active staff with ecommerce.customers receive the stored cart email. Other viewers see a presence flag. Tokens and owner references are omitted for everyone. Email presence is distinct from unreviewed contact eligibility; the page offers no mailto, campaign action or automatic recovery email.

Retention settings at /admin/carts/settings use ecommerce.view to read and an additional system.write or ecommerce.settings permission to save. Current operational defaults are 24 hours inactivity, 30 guest/90 account/7 empty/30 closed days and a cleanup batch of 200. They require controller review and are not universal retention requirements. Saving does not start deletion or reset all existing clocks.

After the cleanup checkpoint migration is applied, operators can inspect count-only previews/status before scheduling bounded cleanup:

TENANT_ENV=k0smos.example.com php bin/console cart:cleanup --dry-run
TENANT_ENV=k0smos.example.com php bin/console cart:cleanup --status

Live cleanup closes expired unprotected shopping carts and deletes eligible closed records after retry horizons. SQL leases/checkpoints protect overlap and restart. Accepted receipts, original settlement references and inventory facts remain protected; oversized dependencies are deferred. After the additional Cart pruning migration, cart:cleanup --prune independently examines bounded primary-key pages of expired drafts/retry receipts/credentials/counters and safe old unprotected shopping links/lines. It shares the retention lease, retains live merge proofs, fixed import claims and empty identity epochs, and resumes from durable cursors. Exact-expiry drafts remain valid; malformed metadata requires repair. Run another normal retention pass after eligible histories drain. An active native settlement owner now permits converted shopping minimization after closed retention and retry/import/merge horizons. Items, shopping email/coupon/subtotal and ownership metadata are erased once; original recovery ID/token, currency, clocks, accepted receipts and financial/inventory facts remain. Invalid or unresolved evidence retains data for reconciliation; newer carts stay intact. Required Payment/Cart/Ecommerce migrations, policy review and read-only preview precede activation. After Cart's additional health migration, read-only cart:cleanup --health and --prune --health report overdue/stalled passes, failures and completed protected/deferred trends, with nonzero attention exits. At most 50 count-only fixed errors per mode survive recovery. Counts are observations, not a live inventory; no customer/cart/payment identifiers appear. Lease renewal is not progress. Backup erasure reapplication, scheduling, external alert delivery and tenant activation are separate operator actions; see the command inventory.

Customer access and account pages

Shop customers and framework staff are separate identities. A customer password does not grant /admin access, and a staff sign-in or matching email does not establish customer ownership.

Register and sign in

Use the shop's account link or /shop/login. Registration at /shop/register collects email, password, first/last name and optional phone, creates a registered customer and starts the customer session. The shared password minimum is eight characters; it is an application policy. Existing-email and supported validation errors appear in the flow. Native login/registration also work without scripts. Registration, profile updates and staff customer forms validate email syntax, required names and existing field lengths on the server. International names and punctuation are supported; malformed field types and control characters are rejected. Email/names allow 255 characters, optional phone 120. This does not verify email ownership or validate every international telephone format.

Imported accounts can already exist even when they cannot yet use a password. If the shop shows the inline password-setup notice above the login form, use its reset-link action rather than repeatedly registering the same address. A migrated password prompts an optional change; continuing remains available. No sign-in notice is sent by email or displayed as a modal.

Current signup starts a session without proving email ownership. Do not treat registration or an editable profile email as verified contact evidence. Complete contextual privacy notices and country-sensitive purchase validation remain separate delivery requirements.

Logout accepts the same internal continuation from native forms and JSON requests. Missing or non-text destinations use the storefront fallback. External URLs and paths with backslashes or control characters are not accepted as continuations. Refused authentication requests leave the current identity intact.

Password change and recovery

“Forgot your password?” opens /shop/password/forgot. When delivery is available, an active account can receive a single-use link valid for one hour. The public answer does not reveal whether the requested address has an account. Repeated attempts are temporarily limited. Opening the email link redirects to a clean reset form tied to the current browser session. Separate links stay in their own tabs; a missing or expired session asks for a new link. Old open forms may ask you to enter the new password again after the update. The form works without JavaScript and confirms the new password; successful reset changes the credential, discards imported hashes, starts a regenerated customer session and can convert an existing guest record into a registered customer. It does not revoke other devices' existing sessions. Successful reset consumption now retires sibling links in the same credential transaction, so concurrent reset attempts cannot both change the password. Profile/admin changes, imported sign-in, connector refresh, reset issuance and imported-hash retention now share current Customer locks and atomic related writes. Imported data cannot replace a password read before another operation changed it; an email change revokes previous links atomically. Independent SQLite process checks cover these writer overlaps and current-recipient/timestamp reads. Other SQL backends and deployed session/provider behavior remain separate qualification requirements.

The email uses configured tenant delivery and its queue. Delivery requires mail.enabled, mail.delivery_enabled and an SMTP host. Without available delivery, recovery is visibly unavailable and no link is created; contact the shop for its staff-assisted process. Staff can change a password from the customer edit form. A failed delivery never silently changes the public account.

When no email worker is available, the same handler runs during the request. Queue acceptance or a staff “sent” result does not prove inbox delivery.

Profile, addresses, orders, wishlist and reviews

At /shop/account, customers see their profile and account navigation. Rejected profile saves show translated errors beside the affected fields and preserve the other entered values, including without JavaScript. Passwords are never restored; leave the password blank to keep the current credential. An expired session token asks for a reload before retrying. Input refusals do not change the profile or session; accepted profile and password changes are separate writes.

Order history is at /shop/account/orders; saved addresses are managed under /shop/account/addresses, and wishlist entries under /shop/account/wishlist. Purchased-product reviews follow eligibility and moderation rules. Invoice summaries/links appear only when the Invoice capability is active and the customer is entitled to the referenced order.

Address fields currently cover recipient/company, street, postal code, city, province, country and phone. Country is free text and defaults to Italia. Account address create/update/delete require the current session token and same-origin guards. Address text is checked on the server for raw types, UTF-8, controls and existing field lengths before saving. Recipient names, street, city and country need visible text. For Italian IT/Italia/Italy values, input is normalized to IT and requires a five-digit CAP plus two-letter province code; province letters become uppercase and leading CAP zeroes are preserved. These Italian rules are not imposed on other country text. Rejected native forms retain the entered address and show translated linked field errors. Partial updates preserve omitted stored values; unchecking the default checkbox now clears that flag. Existing saved/imported rows are not rewritten. Postal syntax does not verify that a CAP/province exists or matches the street, nor qualify delivery availability, importer writes, fiscal data or deployed storefronts. Customer order history currently loads the account's list without the backoffice's SQL pagination; large-history pagination remains a limitation.

Wishlist controls require their browser enhancement and rebuilt theme assets. Adding sends one product/variant unit through the strict Cart protocol and keeps the saved wish on success or refusal. An uncertain outcome retries the same operation on its original row; that wish cannot be removed until the retry resolves. Removal is a separate explicit action. Changed accounts hide old rows and require reload. Deleted products remain unavailable and removable; a removed variant links to product selection rather than silently adding its parent. The obsolete authenticated /to-cart endpoint returns 410 without side effects. Saving/removing a wish requires wishlist-specific CSRF, same-origin checks and frozen page identity controls. The server verifies the current active registered customer and item ownership; old controls after an account switch, guest/inactive accounts and foreign removals refuse. Cart revisions leave valid wishlist controls usable, and wishlist writes do not change the cart or reserve stock. Source checks do not establish deployed browser/device or broader profile/address write qualification. Approved reviews alone publish; submission requires an owned, non-cancelled order containing the product, rather than proof of payment or delivery. Customer invoice detail can link to an upstream document when available; a generated native PDF is not guaranteed.

Checkout, payment and completion

Review the canonical cart, then open /checkout. Guests and registered shoppers can enter a new shipping address. Registered shoppers can also select a current owned saved address or edit it for this order only. Billing defaults to the shipping facts and can instead use a separate new, saved or order-only edited address. Choosing an edit source and updating the summary loads that owned row once; subsequent previews preserve your edits. Saving address-book changes is a separate account action.

Each current checkout produces a shipment. Fresh confirmation requires recipient first/last name, street line one, city and country for both resolved roles, with bounded optional company, second street line and phone. Italian addresses also require a five-digit CAP and two-letter province code; IT, Italia and Italy normalize to IT. Other country text keeps its existing behavior. This does not establish supported countries, universal postcode/province requirements or digital-only supplies. All controls have labels and field limits; rejected native submissions retain safe drafts and linked translated errors. Use “Update summary” while completing the form. Native preview and coupon removal work with incomplete fields without JavaScript; confirmation stays disabled until the address review is ready. After changing country, update the summary before confirming. Incomplete saved Italian addresses can be corrected using the order-only edit choice; the saved address book and historical accepted captures remain intact. These format rules follow the Poste Italiane address standard and do not validate fiscal identity. The completed summary displays all nonempty postal facts for both roles before payment. Inline addresses have null legacy address-book IDs but are captured as immutable accepted facts.

Review issues a ten-minute, session-bound quote. The final native action is “Conferma e paga” / its translated equivalent. Confirmation rechecks the cart version, customer/address ownership and current commercial facts before committing coupon usage, stock holds, order, purchase and one checkout receipt. Later catalog edits do not reprice accepted order snapshots. Changed or expired quotes require a new review. Zero-total purchases use no_charge without a payment gateway.

Selected saved shipping/billing addresses also bind their current recipient, postal and contact facts to the review. Editing those facts under the same ID requires a new confirmation; changing an address alias or default preference does not. The quote stores only its existing hashes, and accepted receipt replay does not resolve today's address book again. Newly accepted orders also capture immutable postal facts in the same acceptance transaction. Order detail and Invoice assembly preserve captured billing/shipping after address edits/deletion, including explicitly absent roles in historical captures. Fresh current shipment acceptances require complete resolved roles. Historical orders without capture retain legacy live-address fallback; no historical data is fabricated. Apply Ecommerce Version20261013120000 before deploying these readers/writers and retain its accepted records during code rollback. Fiscal snapshots, reviewed retention/ export/erasure remain unfinished. Twelve independent-process SQLite source tests qualify saved-address edits, deletion and ownership transfer before/during acceptance: current facts/ownership still govern refusal before effects, a writer is blocked during acceptance and later changes preserve accepted postal facts / receipt replay. Selected owned saved/edit facts now use complete current row reads with transaction locks on MySQL/PostgreSQL. Twenty-four independent-process MySQL cases qualify the actual reader/review/capture boundary and leave unrelated rows writable. Retryable transaction refusal rolls back and asks for renewed review before provider effects. Full backend checkout/install/upgrade, PostgreSQL execution, simultaneous authenticated HTTP and deployed deadlock/timeout recovery remain to qualify. The private native review intentionally shows postal values; JSON responses provide readiness/error codes without those values and quote SQL remains hash-only. Existing accepted receipts replay without requiring new draft fields or today's address ownership. A changed accepted command reports a conflict with native receipt recovery instead of creating another order. No tenant migration or provider request accompanies source delivery.

Gateway communication follows the acceptance commit. Retries reuse the accepted identity and payment attempt; a lost response does not authorize a second charge or release its stock hold. Every resolved completion page, even while payment is pending, signals current cart state through the compiled client. Existing readers immediately hide stale rows and reload authorized SQL; other tabs receive only a random invalidation signal. No cart token is disclosed and no browser selections are cleared, so an old result cannot erase a newer cart. With blocked storage, local readers still refresh and other tabs refresh on focus. This requires the eight maintainer-compiled front bundles; native result and cart links remain usable without JavaScript. Native payment creation/preparation/publication share a per-purchase SQL guard, including an empty family. Complete local preparation commits atomically or joins checkout's transaction. Apply Payment Version20261012170000 before deploying these writers. Multiple attempts stay supported; protected shopping minimization now has a native SQL evidence evaluator that checks receipt, current intent family, financial snapshots and complete historical hold/movement coherence. Malformed, unresolved, ambiguous or oversized evidence retains data. Cart/Ecommerce Version20261012180000 add required lookup indexes using queued portable index-only DDL. Core Cart DI now selects only an explicit runtime-active settlement owner; cleanup minimizes coherent eligible shopping while preserving recovery stubs and accepted facts. Runtime migration/remote-SQL qualification remain pending. Native callbacks use current locked payment state and commit purchase, order and inventory publication together. Payment header updates preserve accepted purchase item IDs and any linked records; they never replace commercial lines. A local storage/inventory failure with a confirmed rollback preserves the accepted reference. If BEGIN/COMMIT or rollback leaves the SQL outcome uncertain, the runtime retires its connection and stops without receipt/failure writes, automatic retry or page-layout queries. JSON checkout and payment callbacks return 503; native checkout redirects with a reconciliation warning. An uncertain queued webhook remains reserved for reclaim by a fresh worker. A lost commit reply may already have persisted settlement; reconcile the original command. The existing receipt recovery path can repair it without another provider start. Old callbacks cannot overwrite newer per-intent captured/refunded state, and replay preserves advanced order states through an atomic conditional update that preserves order lines and commercial snapshots. Owned native SQL diagnostics now verify uncertain queued webhook retirement and fresh stale-job recovery on MySQL/PostgreSQL, using the original private event/intent without repeated stock effects. This isolated in-process path does not qualify production routing, supervision, HTTP/provider behavior or reliable post-commit event delivery.

Uncertain provider outcomes require reconciliation. Operators verify provider evidence/webhooks rather than create a replacement charge or infer failure from a timeout. Payment notifications are best effort, without a durable exactly-once delivery guarantee.

Only configured, enabled methods are offered. Configure the native Payment owner under its settings; provider credentials use the shared secret-management boundary. Verify sandbox success, refusal, cancellation, lost responses, webhook handling and recovery for each deployed provider. See payment architecture and secret management.

Signed payment-result links exchange before rendering into a clean reference valid only in the current tenant/session. Independent result tabs retain their original signed expiry; each reload checks current local payment facts without calling the provider. Session-bound accepted receipts retain their recovery path. Webhook workers keep private dependency construction, provider processing and SQL inside content-exclusion scopes and export only a fixed failure code to queues. Reset/Notification email producers and lazy workers also exclude private content and export fixed errors; inline fallback attempts delivery once. Notification jobs still contain private delivery inputs and failed jobs retain their payload. Initial provider URLs/history, dead-letter retention, deployed native sessions and proxy/provider logs require separate qualification.

Fiscal and market limitations

Implementation and qualification now start with Italy; the broader requested target remains private/business customers across Italy, other EU and non-EU destinations, for guests and accounts. This is intended coverage, not evidence that every country, supply type or tax regime is currently supported.

Customer/address, invoice and provider-mapping contracts still lack complete Italian tax code, VAT/business/foreign identifiers and electronic-invoice routing. Invoice assembly currently uses N/D for some missing address facts; enabling Invoice does not repair these gaps. Complete immutable billing/fiscal snapshots, conditional country/type fields, destination rules and provider payload qualification remain pending. The shop must establish seller jurisdiction, tax regime, individual supported markets and invoice policy before enabling the corresponding workflows.

Backoffice workflows and permissions

Default and Sober share the native admin contracts. Open /admin/ecommerce for the task-based dashboard, then the relevant catalog, sales, customer, marketing or configuration area. Data/actions follow their owners' permissions.

Work Entry point Main permission
Read catalog and cart activity /admin/catalog/products, /admin/carts ecommerce.view
Edit catalog and import conflicts /admin/catalog/products/{id}/edit, /admin/catalog/conflicts ecommerce.edit
Customers and password assistance /admin/customers ecommerce.customers
Orders and fulfilment /admin/orders ecommerce.orders
Shipping methods /admin/shipping ecommerce.shipping
Coupons and promotions /admin/coupons, /admin/promotions ecommerce.marketing
Currencies and VAT rates /admin/currencies, /admin/tax-rates ecommerce.settings
Review moderation /admin/reviews ecommerce.reviews

Permissions to manage promotion rules are not customer marketing consent. Forms use native POST/redirect or an explicit JSON success response for the enhanced runtime. CSRF, validation and conflict checks still apply to writes.

Create and maintain customers

Open Customers and choose create at /admin/customers/create. Enter email, names and supported contact fields; optionally enter the first address. A registered record requires a password of at least eight characters in this backoffice form. Choosing guest stores no password and creates a record without password login. Creating a Customer never creates a framework staff account.

An anonymous shopper's bound guest cart, an admin-created guest Customer record and a guest checkout are distinct. Unauthenticated checkout has null Customer and legacy address-book references, while its required inline postal facts are captured with the accepted order. It does not automatically create a Customer or claim an existing account by email. Guest Customer records normally have no password; explicit staff edits or password reset can change their credential state.

The list filters active/guest, credential state and import source; counts, spend by currency and order history use the backoffice read model. The customer page provides details/history, edit and password-reset assistance. Edit can change active/guest flags or set a password. “Send password reset link” reports sent, skipped, failed or unavailable so the operator can investigate delivery and retry. It is a protected individual action, not bulk sending.

Imports and integration ownership

Psapi imports supported PrestaShop catalog/customer/order data; Wpapi maps supported WooCommerce entities. Data Exchange also transports the portable catalog with canonical tax codes, category slugs, product SKUs, variants and image slots. Imports preserve local identifiers and source mappings. The portable catalog providers cover tax rates, categories, products, variants and product images. They do not export customers, carts, passwords or invoices. Inventory CSV export is a separate protected admin action, not a public merchant/product feed.

Source-owned fields refresh from the connector; tenant-owned fields stay local. Overridable fields follow the source until locally changed/protected, after which source differences become reviewable conflicts. Operators can retain local data or apply the source. A whole-product import lock freezes that entity and its imported descendants. Localization provenance and ownership are locale-scoped. Search projections do not replace SQL ownership.

Supported PrestaShop hashes can authorize the first imported-account login; k0smos then creates its local hash and deletes the imported hash. Local passwords take precedence. Unsupported/malformed schemes and WooCommerce accounts use password setup instead. Credentials and connector secrets are excluded from portable catalog exports. Unused imported hashes have a bounded purge command with a default 365-day period; review that policy before scheduling it.

TENANT_ENV=k0smos.example.com php bin/console ecommerce:customers:purge-imported-credentials --dry-run

For release-specific import commands and retry/checkpoint behavior use the connector reference and Data Exchange. Actual source credentials, provider payloads and imported entities require connector qualification.

Customer accounts, carts and settlement recovery contain private data. SQL or first-party cookies alone do not settle a processing purpose or retention policy. Private cart, account, authentication/password and checkout/payment roots receive private/no-store, noindex and no-referrer response headers independently of the theme, including redirects, errors and disabled modules. This does not remove bearer references from every recovery URL or qualify workers/proxy access logs. Cart email, signup, login and an attempted purchase are not reviewed contact eligibility. No automatic cart campaign is delivered by this guide.

Klaro now has a bounded data-only service/vendor/storage registry, preserved migration preview and atomic current-revision saves with mandatory preview/save CSRF and auditing. Default/Sober inherit the compiled review-before-save editor; public shells emit only validated script-safe JSON. All eight public entries share recoverable startup and translated footer reopen controls. Restored pages reconcile saved choices before notifying media; absent/invalid choices remain denied without rewriting the saved record, and pending Play is cancelled on departure. Deployed owner-built/BFCache/provider qualification remains separate. Missing/unmanaged optional provider frames are blocked. DM1's server-rendered YouTube/Vimeo frames now always start without src, and legacy consent flags cannot bypass its activation check. Local videos retain their native sources. Removing a service or revoking consent tears down loaded media. Queued lock anchors, reviewed configuration conversion and owner-built affected bundles must roll out together. Source delivery does not activate consent or migrate a tenant. A banner does not implement registration information, marketing evidence, country-specific invoicing or complete export/erasure workflows. Optional-service inventory, prior network/storage gating, withdrawal cleanup, versioned evidence, contextual notices and remaining worker/log redaction qualification are still unfinished. Provider SDKs, remote media/fonts and deployed themes must be observed against the tenant's reviewed configuration. CC1 display/icon fonts and PC1 icon fonts now have pinned local stylesheet entries, replacing external head links and preconnects in source. Deploy complete owner-built assets with the new heads; production sizes/delivery remain unqualified. Default maps now require exact configured osm_leaflet/google_maps boolean permission before tile/SDK construction. Local coordinates, preferences and explicit retry remain available; revocation/removal and late SDK work are fenced. Restoration waits for reconciled choices, including late capability installation. Deploy owner-built Default assets with helper markup and a reviewed registry. Real provider/network/storage/BFCache acceptance remains separate. Predictive query/selection measurements now require declared search_measurement cookie permission on the server and strict permission in the shared client; requested search remains usable. Catalogue measurement defaults off. Withdrawal/restoration clears correlations; denied direct POSTs discard payloads before processing. Permitted events now use a typed recorder and separate bounded local sink with fresh permission checks and contained write failures. Early query classification excludes raw application HTTP/SQL/debug/error/provider-failure diagnostics, including disabled Ecommerce and core public search. Query-free catalogue responses retain public behavior. LocalStorage server evidence, retention, external proxy/provider logs and owner-built rollout remain separate gates. Authored Page/Widget HTML, CSS and supplied media URLs need their own delivery inventory. Form-protection CAPTCHA needs an explicit provider classification; optional refusal must preserve protected forms and requested checkout. This source inventory does not certify the deployed site's requests or approve a legal policy.

Public search and documentation projections use reviewed public sources. Source checks now prove customer email/address, order/payment references and cart tokens/ snapshots stay outside sitemap, JSON-LD, public search, Discovery adapters and vector backend-port documents. Private/bearer canonical paths are refused; changed or withdrawn catalog content invalidates stale Discovery references. A storefront Customer session does not authorize MCP tools. Ecommerce/Cart currently publish no merchant feed or MCP tool. All-eight head render fixtures exclude private account/cart/reset state from commerce metadata. Real provider/ vector-store, active public Discovery HTTP, tenant content and future merchant feed qualification remain separate; these checks approve no legal policy. See cookie-consent integration.

Theme authors and rollout checks

Diagnose common failures

Symptom Check and next action
Two shoppers request the last unit With one sellable unit, acceptance must reserve it once and refuse the competing request. Refresh the refused cart and review availability; changing its browser quantity does not authorize another sale. Qualify simultaneous requests on the deployed SQL engine.
An old tab cannot confirm Reload the canonical cart and review its current version, owner and expiry. A stale version or expired quote requires another review, not replaying edited hidden controls.
A language link reaches the wrong page Use generated locale links and check the tenant's default/supported locale policy. Catalog translation fallback and English-only documentation content are separate from navigation language.
Payment times out after confirmation Preserve the accepted order/payment reference and reconcile provider evidence or webhooks. Retry the existing recovery path; do not create another charge or release the accepted inventory hold from a timeout alone.
Source markup is correct but controls behave differently Check the selected theme, deployed manifest and served asset hashes with the frontend-delivery procedure. Have the maintainer build/deploy the changed bundle, then recheck the journey; clearing a browser cache does not compile missing assets.
A reset email was accepted but never arrives Check tenant mail configuration, the email worker and delivery failure records. Queue acceptance does not prove delivery; investigate before retrying the protected individual reset action.

Reuse the module's canonical catalog, pricing, account and checkout payloads. Do not implement a second price/stock authority, infer Customer identity from staff/email, or treat a browser snapshot as permission to overwrite SQL. Keep native forms, server conflict recovery, generated locale URLs, canonical menu ownership and the session-bound accepted-payment recovery contract.

The maintained storefront shells are Default, CC1, DM1, DM2, LTS1 and PC1–3. LTS1 is the first concrete ecommerce concept. A theme's render test is not complete shop qualification. Changed CSS/JavaScript requires the maintainer's build, asset verification and deployment; publication of this Markdown requires no theme build. Use template data and frontend delivery for extension boundaries.

Before rollout, qualify a fresh install and an upgrade, migrations and backups, native/script-enhanced journeys, both locales, denied browser storage and identity changes. Exercise provider failures/retries and restore/merge/checkout/ cleanup concurrency on supported SQL engines. Verify configured private/business, guest/account and country cases with required address/fiscal facts and notices. Record unsupported cases, remaining assets and provider/device checks explicitly. Retaining a success page or passing isolated tests does not prove deployment or legal conformity.