Intelligence, apps and payments · v0.5
This document describes implemented paths and explicit prototype limits.
One connected shop memory
flowchart LR
Order[Order or confirmed sandbox capture] --> Event[Transactional outbox]
Event --> Worker[Receipt-based projection worker]
Worker --> Evidence[Order association with event and simulation provenance]
Evidence --> Graph[PostgreSQL relationship and persistent hypothesis]
Graph --> Merchant[Shop intelligence and grounded merchant chat]
Merchant --> Review[Explicit merchant decision]
Review --> Public[Product recommendations and customer concierge]
cognition/projection.rs deduplicates both event receipts and order-pair evidence.
The same order cannot be counted twice when its placement and capture events
arrive. Pending external payments enter knowledge only after confirmed capture.
Current manual/simulated and sandbox evidence stays labelled as such. Graph
writes, evidence and receipt commit together with outbox delivery.
GET /api/intelligence exposes at most 24 pairs/hypotheses, observed order counts,
simulated counts and source event IDs to an authorized merchant. A revision-bound
PUT /api/intelligence/hypotheses/{id} records dismissed, experiment or published
with approve: true. Experiment means marked for review; no randomized trial
starts automatically. Published pairs feed /store-api/intelligence/recommendations/{sku}
and the customer concierge. Private order identities/counts never enter that
public response. The merchant can dismiss a published pair again.
Memory changes retrieval and registered decisions, not model weights. No causal sales uplift is inferred from co-purchases. Session layout policy remains the separate small persisted epsilon-greedy policy.
The planner receives up to 24 localized root products, bounded history, observations and active app contracts/records. An app save can become a typed proposal; authoritative app and record revisions are attached by the server. Approval applies core and app changes in one transaction. A stale record or package rejects the whole transaction. Remote service writes are not part of this atomic planning path and need a future explicitly approved saga contract.
Versioned apps and three execution boundaries
- Managed declarations: an API-1 manifest defines typed entities, foreign references, actions, permissions and slots. The installer creates real tables, indexes and compound tenant foreign keys. New nullable columns can be added; incompatible changes/removals/downgrades fail. Versions are immutable within a shop; compatible versions coexist across shops over a shared physical superset.
- External services: operator-configured HTTPS (or explicit loopback during development) services run in a separate process with their own storage/UI. The core supplies a tenant and service credential to declared actions. Events are durable, at least once, leased and keyed for receiver deduplication.
- Pure Wasm: the existing B2B approval hook keeps its no-import/fuel/memory restrictions. It does not gain arbitrary host access from the app platform.
Managed app tables use forced PostgreSQL RLS with transaction-local rac.tenant.
Core tenant tables now also have forced policies with request-bound pool hooks;
application object/customer checks remain required. Strict runtime startup rejects
core ownership, bypass roles and TRUNCATE privileges. SQLx uses a checked identifier
builder; merchants and models never submit SQL or arbitrary Rust/PHP. A database
superuser bypasses RLS, so enable the separate non-owner runtime as described in
the illustrated architecture and deployment guide.
The same registered action serves /api/apps/{app}/actions/{action}, public
/store-api/apps/{app}/actions/{action} where explicitly allowed, and MCP
app.{app}.{action}. Readers can list private app data but cannot save or invoke
private service actions. Editors may change app records; installation/lifecycle
requires owner/administrator authority. Editor-writable app data must not contain
credentials or security configuration; operational secrets are operator-owned.
Admin UI panels can use managed forms or an operator-configured iframe.
The iframe has an opaque origin (allow-scripts allow-forms, no same-origin),
no bearer token and a source-window/nonce-bound message bridge. The SDK can invoke
only the current app's declared actions through normal server authorization.
Own UI bundles now mount at declared admin/storefront surfaces, with namespaced API aliases and selected AI context; see app-platform.md. They cannot inject code into the parent document. A resource-limited example container is provided; Canonical package signing and bounded read-only WIT commerce hooks are implemented. MicroVM isolation and automatic PHP transpilation remain absent. See security boundaries. Deployment network/runner boundaries remain the operator's responsibility.
App-specific engraving validation lives in extensions/apps/engraving/configuration.wat and its manifest. The generic host in src/apps/cart_contributions.rs binds revisions and applies the result; src/apps/runtime.rs executes the package ABI. An independent gift-message package uses the same contract with different rules and input fields.
The engraving example is genuinely connected: product slot → authoritative configuration → taxed cart surcharge per unit → revision check → immutable order configuration → merchant order view. Removing the line removes the fee; old configuration records remain in that cart until the cart is discarded.
Payment adapter and ledger
Payment apps now use the general provider API 1: versioned contracts, tenant/channel onboarding, frozen environments and accounts, redirect/embedded checkout, exact receipt allocation and protected app/Flow/MCP commands. Shopware Payments provider code and attribution remain in the private repository; the public build has no dependency on that repository.
payments/provider.rs::PaymentProvider is the native adapter boundary. The core
owns amounts, order/cart authorization, idempotency, inventory and ledger states.
The provider owns wire calls. The first adapter is PayPal Orders v2, with configured Sandbox or Live environments.
This is not Shopware Payments. Actual PSP account traffic remains unverified.
Checkout atomically creates an immutable EUR-cent attempt, decrements and records reserved SKU quantities, and queues a create job. A separate worker performs network calls after committing its claim. Stable provider request IDs, attempt serialization, leases and fenced receipts prevent duplicate local effects. Capture reconciles PayPal first, so a lost capture response can recover without a second charge. Only an exact provider order/currency/amount/merchant receipt marks paid. Browser redirects cannot do that. Cancellation reconciles before releasing stock once. A late capture moves the order to payment review.
Refund requests reserve the requested cent amount against the remaining captured balance, deduplicate replays and reject over-refunds. Webhooks first call PayPal's signature-verification endpoint, then persist an idempotent inbox and queue reconciliation. This follows the Orders API, idempotency and webhook verification contracts.
Configure server-only PAYPAL_ACCOUNTS (or legacy PAYPAL_SANDBOX_ACCOUNTS) as a JSON object keyed by exact
workspace ID, with clientId, clientSecret, webhookId, explicit private bnCode,
environment and optionally merchantId.
Install the PayPal app in that shop and enable its configured method in Settings → Payment methods. Set PUBLIC_BASE_URL to the browser-accessible origin. No credential is
returned to the browser or sent to a model. The return flow relies on the original
browser's cart context; hosted-agent handoff still needs a secure transfer token.
{"my-shop":{"clientId":"...","clientSecret":"...","webhookId":"...","merchantId":"...","bnCode":"your-authorized-private-attribution","environment":"sandbox"}}
The environment chooses https://api-m.sandbox.paypal.com or
https://api-m.paypal.com; Live additionally requires HTTPS return URLs. Only an explicit
loopback override is allowed for contract tests; such attempts are permanently
labelled contract-fixture. Environment/version mismatches require the original
adapter for reconciliation. No actual Sandbox or Live transaction was run.
Shopware's Payments documentation
requires a valid Shopware installation and onboarding. A standalone integration
contract has not been verified. Its example app therefore reports
connector-contract-required and cannot be selected for checkout. The native
PayPal adapter must not be presented as an official Shopware Payments integration.
The current provider API adds authorization/void commands and multi-currency with immutable precision. The following gaps refer to the native PayPal adapter: dispute and externally initiated refund reconciliation, asynchronous refund completion, early webhook matching before provider-ID persistence, refund retry beyond the provider's idempotency retention, multi-seller onboarding/credential rotation, and a real sandbox-account end-to-end run. Uncertain operations retain inventory or refund reservations; they require reconciliation rather than guessed success.
Process scaling and measured boundaries
PROCESS_ROLE selects all (local default), http (HTTP and diagnostic
counter flushing), memory-worker (core outbox and projections), payment-worker,
app-worker (flows, schedules and app deliveries), translation-worker or
media-worker. The HTTP-only role does not consume the durable outbox. Worker roles do not bind an HTTP
port. Shared SQL leases/receipts coordinate replicas. Example configuration and
an independently running SQLite service are in extensions/README.md.
Chat admission uses a short DB lease, at most two concurrent conversations per shop and four inference slots per process. Long model calls hold no DB transaction or connection. Context/replies have bounds; service/payment JSON responses are stream-limited to 64 KiB. API compute, local inference and remote payments can therefore progress independently.
These changes remove concrete bottlenecks; they do not establish a speedup over Shopware. Catalog/detail/cart reads are now bounded and serving is separated from migrations. Million-product commerce probes and small-fixture read-context comparisons are measured separately. Private Qdrant search replaces exact pgvector ranking; semantic quality/scale, sustained many-tenant capacity and HA remain unmeasured. Historical aggregates and some searches still grow with matching data. Global inference admission is per process, not distributed. The next performance gate is comparable release-build workloads with identical cart semantics, database and catalog size; report p50/p95/p99, throughput, CPU/memory and model latency separately.
One-page checkout, approval return and the private Shopware Payments boundary.