@laioutr/app-b2bsellers
v0.3.0
Published
B2B Sellers connector app for Laioutr
Readme
B2B Sellers App for Laioutr
A Laioutr App for integrating B2B Sellers into the Laioutr platform.
It wraps the B2B Sellers Store API (a Shopware 6 plugin) as a typed operations map with thin query
wrappers and a request-scoped client. Backend only — no sections, no blocks. Its consumer is
app-boltze.
See laioutr.com for more information about Laioutr.
State of the integration — 2026-09-17
Verified against https://laioutr.demoshop.b2b-sellers.com (Shopware 6.7.6.2) with a live customer
session. Full evidence in docs/reviews/2026-09-17-store-api-verification.md.
Reading works. 90 operations, zero missing paths; 27 return real data with a session — 12 customers, 10 orders, 17 employees, 3 cost centers, 2611 activity records. The map was corrected against the shop rather than against its OpenAPI document, which disagrees with it in a dozen places.
Writing does not, and that is not a code problem. Three independent routes into creating an offer are each closed:
| | |
| --- | --- |
| POST /offer then offer-add-products | the offer is created, then 403 B2B_OFFER__UPDATE_DENIED in every state, and PUT /offer/{id} is 405 — so it stays permanently empty |
| POST /offer-request (cart → offer) | 403 B2B_OFFER__PERMISSION_DENIED for a B2B employee and for the B2B administrator. None of the 15 employee permissions the shop exposes concerns offers, so it is an admin-side setting |
| any of them | the Headless sales channel our access key belongs to sells one product; everything on the existing orders belongs to another channel and the cart refuses it with product-not-found |
What unblocks it — Shopware admin work on the demo shop, by whoever provisioned it: assign the
catalogue and its visibility to the Headless channel and enable the offer module for its B2B
customers, or hand over the access key of the channel that already carries the catalogue
(ee0ad685d0504d5f80730d9b53f794c8).
Also worth knowing: the shop rate-limits sign-ins hard (429 CHECKOUT__CUSTOMER_AUTH_THROTTLED)
after a handful of attempts. A few mistyped passwords on stage will lock an account out for minutes.
Credentials
The demo shop publishes its own test accounts at /Benutzeruebersicht/. The sales app signs in as the
Vertriebsmitarbeiter [email protected]. [email protected] is deliberately refused — the shop
denies it B2B-platform access on every route.
Checking a shop yourself
pnpm verify:openapi # the operations map against the shop's own OpenAPI document
pnpm verify:shop # the operations map against the shop itself, read-onlyBoth read SHOPWARE_SHOP_URL and SHOPWARE_ACCESS_KEY from the environment, and both take flags
straight through — pnpm verify:openapi -- --save=openapi3.json, pnpm verify:shop -- --user=… --password=….
export SHOPWARE_SHOP_URL=https://laioutr.demoshop.b2b-sellers.com
export SHOPWARE_ACCESS_KEY="$(node -p "require('./laioutrrc.json').apps[0].config.accessToken")"Why this exists. src/runtime/server/client/operations.ts claims, for 95 operations, that a given
name means a given method on a given path with a given response. Those claims were written without a
live instance — and a wrong one is invisible: TypeScript is satisfied, the linter is quiet, and the
unit tests mock the client away. It surfaces only at runtime, as a 404 that reads like "the plugin
cannot do this" rather than "the path is misspelled". This script makes the shop the authority instead
of the vendor's documentation, and turns a silent lie in the types into a non-zero exit code.
It has earned its keep: it found 29 operations missing the /store-api prefix and 11 more under an
undocumented sales-representative one — forty dead routes — and later caught a body this repository
declared optional where the shop requires it.
verify:openapi fetches GET /store-api/_info/openapi3.json and compares every entry. Verdicts:
OK, MISMATCH (status, body or parameter names differ), WRONG_METHOD, PREFIX_MISSING (the path
exists but the map omits the server prefix, so the call 404s), PATH_MISSING. --spec=openapi3.json
runs it offline against a saved document, --fields prints the response's top-level fields, --all
lists the spec paths the map does not cover, --json is machine-readable. Exit: 0 clean, 1 mismatches,
2 could not run. Today it exits 1: 16 mismatches and 2 wrong methods are still open, so treat
the run as a report to read rather than a gate that has ever been green.
verify:shop is the stronger claim: it calls the shop rather than reading its document, because a
route can match the document and still 404 — order-approval list and create did exactly that. It
skips every mutating operation by name, so it stays read-only; with --user / --password it signs in
and reads real data.
The name is about OpenAPI, the specification format Shopware serves about itself. Nothing here talks to OpenAI and no API key of that kind is involved — the only credential is the shop's own sales-channel access key.
Features
Backend only — a typed Store-API surface, not a storefront app. No Orchestr
handlers, no sections, no blocks live here; the consumer (app-boltze) writes
those and calls the client this module exposes.
- 🔌 89 typed Store-API operations with thin query wrappers, verified against a live shop
- 🔑 Request-scoped client that holds a customer session (
useB2bSellersClient), the secret server-side only - 🧪 Two verification scripts — one against the shop's OpenAPI document, one against the shop itself
Quick Setup
Before installing dependencies, you need to create a copy of the .npmrc.config file called .npmrc and fill in the NPM_LAIOUTR_TOKEN with your npm token. You can find this token in your project settings.
pnpm inpx @laioutr/cli project fetch-rc -p <organization slug>/<project slug> -s <project secret key>- This will load thelaioutrrc.jsonfile with the current remote project configuration.pnpm dev:preparepnpm orchestr-dev
That's it! You can now use the B2B Sellers App in your Laioutr Frontend ✨
You can find a thorough guide on getting started with Laioutr development in our developer guide.
Linting and Formatting
We use ESLint and Prettier to lint and format the code. This repository contains opinionated configurations for both tools. You can - of course - replace them with your own configurations.
Publishing
Releases are managed with Changesets and published to npmjs.org automatically by the release workflow.
To ship a change:
- In your PR, run
pnpm changesetand follow the prompt to record the change and the version bump (patch/minor/major). Commit the generated file in.changeset/. - Merge the PR to
main. The release workflow opens (or updates) a "Version Packages" PR that applies the pending changesets to the version andCHANGELOG.md. - Merge the "Version Packages" PR. The workflow builds the package and publishes it to npmjs.org, tags the commit, and creates a GitHub release.
Publishing uses npm OIDC trusted publishing, so no npm token is stored in the repository — the workflow needs id-token: write and a trusted publisher configured for the package on npmjs.org.
Contribution
Follow the setup guide to get started.
