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.
Menus and public URLs
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.
Privacy, consent and public projections
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.