Login, Gastkauf und B2B
Unterstützen Sie anonyme Besucher, Gastkauf, Registrierung, Login und Firmenzugang ohne lokale Freischaltungslogik. Diese Seite passt, wenn Ihr B2B-Onlineshop Firmenpreise, Rechnungskauf, Kundengruppen, Ansprechpartner, Passwortwechsel oder freigegebene Lieferadressen abbilden soll. Der Server entscheidet, welcher kommerzielle Kontext gilt.
Nach der Umsetzung lädt der Client nach Login, Logout und Firmenwechsel die serverseitigen Kontexte neu und zeigt nur Preise, Zahlarten und Checkout-Wege, die Workspace für den aktuellen Besucher freigibt.
Voraussetzungen
Klären Sie vor Login-, Gastkauf- oder Firmenzugangsflows:
- Der Zielmandant wurde mit Tenant Init vorbereitet. Für B2B- und Account-Flows sind insbesondere Commerce- und IAM-Gruppen relevant. System > Betriebsbereitschaft zeigt keine blockierenden systemweiten Hinweise.
- Der Storefront-Kontext lädt und zeigt, ob Gastkauf, Login, Firmenzugang und Angebotsanfrage im aktuellen Kanal erlaubt sind.
- Der anonyme, eingeloggte oder firmenbezogene Kontext kann unterschiedliche Kundengruppen, Preislisten, Zahlarten und Steuerlogik auflösen.
- Rechnungskauf braucht serverbestätigten Firmenkontext, Customer Group und Payment Terms.
- Firmenzugangsanfragen sind Review-Kontext. Sie ersetzen keine CRM-Prüfung, keine Kundengruppe, keine Preisliste und keine Steuerfreigabe.
Die fachliche Einordnung von Kundengruppen, Commerce-Profilen und Zahlungsbedingungen steht in Handel- und Commerce-Fähigkeiten.
Grundregel
Anonyme Besucher nutzen den öffentlichen Standardkontext des Sales Channels. Nach Login oder Firmenwechsel können sich Preise, Steueranzeige, Verfügbarkeit, Checkout-Fähigkeit, Zahlarten und Angebotsfähigkeit ändern.
Laden Sie nach diesen Ereignissen neu:
- Storefront Context
- Cart
- sichtbare Produktpreise
- Checkout Eligibility
Gastkauf
Gastkauf ist nur verfügbar, wenn der Storefront Context oder Checkout Eligibility ihn erlaubt.
Für Gastkauf senden Sie im Checkout:
customerEmailbillingAddressshippingAddress
Diese Daten sind transaktionale Cart- und Order-Snapshots. Der Client legt daraus keine CRM-Person, kein CRM-Unternehmen und keine CRM-Adresse an.
Wenn Gastkauf nicht erlaubt ist, führen Sie Nutzer zu Login, Registrierung oder Angebotsanfrage.
Registrierung
Registrierung erzeugt zunächst einen normalen Account-Kontext. Sie schaltet keine vorhandenen Firmenpreise, keine Rechnung und keine B2B-Steuerlogik frei.
POST /api/v1/public/v1/auth/register
Content-Type: application/jsonSenden Sie email und password im JSON-Body. Das Passwort muss mindestens acht Zeichen lang sein.
Nach erfolgreicher Registrierung:
- Laden Sie Storefront Context neu.
- Laden Sie Cart neu.
- Lösen Sie Pricing für sichtbare Produkte neu auf.
- Prüfen Sie Checkout Eligibility neu.
Wenn der Nutzer Firmenzugang benötigt, starten Sie eine Firmenzugangs-Anfrage. Leiten Sie aus Firmenname, E-Mail-Domain, Kundennummer oder USt-ID keine automatische Freischaltung ab.
Login, Logout und Passwort
Für Browser-Storefronts nutzen Sie die allgemeinen Auth-Endpunkte. Die öffentlichen Account-Endpunkte unter /api/v1/public/v1/account/... verwenden danach die aktive Browser-Sitzung.
POST /api/v1/auth/login
Content-Type: application/jsonSenden Sie email und password im JSON-Body. Login erwartet E-Mail und Passwort. Logout beendet die aktuelle Sitzung.
POST /api/v1/auth/logout
Content-Type: application/json
{}Passwort-Wiederherstellung startet mit dem Endpunkt forgot-password.
POST /api/v1/auth/forgot-password
Content-Type: application/json
{
"mail": "kunde@example.test"
}Der zugesandte Code wird über login-digits geprüft:
POST /api/v1/auth/login-digits
Content-Type: application/json
{
"digits": "123456"
}Speichern Sie Wiederherstellungscodes nicht in Logs, Analytics oder Support-Nachrichten.
Nach Login kann der Server einen anderen Kontext auflösen:
- Customer Group
- Price List
- Netto-/Bruttoanzeige
- Steuerlogik
- Verfügbarkeit
- Checkout-Fähigkeit
- Zahlarten
Nach Logout gilt wieder der öffentliche oder normale anonyme Kontext. Verwerfen oder aktualisieren Sie alle preis- und checkoutrelevanten Client-Daten.
Passwortwechsel ist ein angemeldeter Browser-Session-Vertrag, kein anonymer Storefront-Endpunkt:
POST /api/v1/profile/password
Content-Type: application/jsonSenden Sie das neue Passwort im Feld password. Der Endpunkt benötigt eine aktive Browser-Sitzung und akzeptiert ein neues Passwort mit mindestens acht Zeichen. Nach einem reinen Passwortwechsel bleibt der kaufmännische Kontext gleich. Laden Sie Preise, Cart und Checkout Eligibility trotzdem neu, wenn der Flow gleichzeitig Login, Logout, Tenant-Auswahl oder Firmenkontext ändert.
Firmenzugang anfragen
Wenn ein Nutzer B2B-Zugang benötigt, senden Sie eine Firmenzugangs-Anfrage:
POST /api/v1/public/v1/account/company-access/request
Content-Type: application/json
{
"companyName": "Beispiel GmbH",
"contactEmail": "einkauf@beispiel.test",
"customerNumber": "100123",
"vatId": "DE123456789",
"message": "Bitte Zugang prüfen.",
"pageUrl": "https://shop.example.test/firmenzugang",
"honeypot": "",
"filledSeconds": 12
}Beispielantwort:
{
"state": "requested",
"requestId": "uuid",
"status": "pending_review",
"nextAction": "await_approval"
}Die Anfrage ist Review-Kontext. Sie wird intern als Firmenzugangs-Eingang und CRM-Lead gespeichert, ist aber keine Marketing- oder Consent-Einwilligung. Sie schaltet keine Preisliste, keine Rechnung und keine Steuerbefreiung frei. Der Server begrenzt zu viele Anfragen und bewertet einfache Spam-Signale wie gefüllte Honeypot-Felder, unrealistisch kurze Füllzeiten und URL-Muster. Senden Sie filledSeconds immer mit; fehlende oder zu niedrige Werte können als zu schnelle Submission gelten. Der Request-Body darf das konfigurierte Limit public.storefront.submission.max_bytes nicht überschreiten.
Freigegebene Firmen laden
Laden Sie verfügbare Firmenkontexte:
GET /api/v1/public/v1/account/companiesBeispielantwort:
{
"items": [
{
"companyId": "uuid",
"name": "Beispiel GmbH",
"displayName": "Beispiel",
"role": "buyer",
"storefrontAddressScope": "selected",
"storefrontAddressIds": ["uuid"]
}
]
}Wenn keine Firma verfügbar ist, bleibt der Nutzer im normalen Account-Kontext. Rollen steuern, was der Nutzer im Firmenkontext darf:
| Rolle | Bedeutung |
|---|---|
viewer | Darf freigegebene Firmenbestellungen sehen, aber nicht im Firmenkontext einkaufen. |
buyer | Darf Firmenbestellungen sehen und im Firmenkontext einkaufen. |
admin | Darf einkaufen und zusätzlich Änderungsanfragen für Firmenstammdaten erfassen. |
storefrontAddressScope=all erlaubt alle Lieferadressen der Firma. storefrontAddressScope=selected erlaubt nur die IDs aus storefrontAddressIds. Verwenden Sie diese Felder nur zur Anzeige und Vorauswahl. Der Checkout validiert die Berechtigung serverseitig erneut.
Firmenkontext wählen
POST /api/v1/public/v1/account/company-context
<Cart-Token-Header>: opaque-cart-token
Content-Type: application/json
{
"companyId": "uuid"
}Beispielantwort:
{
"companyId": "uuid",
"state": "selected",
"reviewRequired": true
}Nach erfolgreicher Auswahl:
- Laden Sie Cart neu.
- Laden Sie Storefront Context neu.
- Lösen Sie Pricing neu auf.
- Prüfen Sie Checkout Eligibility neu.
- Zeigen Sie Review an, wenn
reviewRequired=true.
Senden Sie für B2B-Kontext den vom Server bestätigten Firmenkontext. Erfinden Sie keine companyId und verwenden Sie keine Kundennummer als Firmenkontext.
Eine Identity mit aktivem Firmenzugang wird im Checkout als Firmen-Identity behandelt. Privater Einkauf mit Personenadressen ist für diese Identity nicht vorgesehen und kann mit COMPANY_CONTEXT_REQUIRED abgelehnt werden. Legen Sie für privaten Einkauf eine getrennte Identity ohne aktiven Firmenzugang an.
Einladung für Ansprechpartner annehmen
Ein Ansprechpartner bekommt eine eigene Identity. Es gibt keinen gemeinsamen Firmenlogin.
POST /api/v1/public/v1/account/company-invitations/accept
Content-Type: application/jsonSenden Sie Einladungs-Token, E-Mail-Adresse und neues Passwort im JSON-Body. Das Passwort muss mindestens acht Zeichen lang sein.
Beispielantwort:
{
"state": "accepted",
"identityId": "uuid",
"tenantId": "uuid"
}Speichern Sie Einladungs-Tokens nicht in Logs oder Analytics. Akzeptieren Sie Einladungen nur mit dem dafür vorgesehenen Formular und der serverseitigen Prüfung.
Rechnung
invoice ist nur verfügbar, wenn der Server sie in Checkout Eligibility oder Storefront Context erlaubt.
Rechnung setzt typischerweise voraus:
- bestätigten Firmenkontext
- Customer Group am Cart
- aufgelöste Payment Terms
Schalten Sie Rechnung nie lokal frei. Wenn invoice nicht erlaubt ist, zeigen Sie erlaubte Zahlarten wie Vorauskasse, PayPal, Kreditkarte oder Angebotsanfrage.
Kundenkonto
Angemeldete Nutzer können Bestellungen und eigene Profildaten über Public Account APIs lesen und pflegen:
GET /api/v1/public/v1/account/orders
GET /api/v1/public/v1/account/orders/{orderId}
POST /api/v1/public/v1/account/orders/{orderId}/reorder
GET /api/v1/public/v1/account/products/purchased
GET /api/v1/public/v1/account/product-lists
POST /api/v1/public/v1/account/product-lists
GET /api/v1/public/v1/account/product-lists/{listId}
PATCH /api/v1/public/v1/account/product-lists/{listId}
DELETE /api/v1/public/v1/account/product-lists/{listId}
POST /api/v1/public/v1/account/product-lists/{listId}/items
PATCH /api/v1/public/v1/account/product-lists/{listId}/items/{itemId}
DELETE /api/v1/public/v1/account/product-lists/{listId}/items/{itemId}
GET /api/v1/public/v1/account/profile/contact
PATCH /api/v1/public/v1/account/profile/contact
GET /api/v1/public/v1/account/profile/addresses
POST /api/v1/public/v1/account/profile/addresses
PATCH /api/v1/public/v1/account/profile/addresses/{addressId}
DELETE /api/v1/public/v1/account/profile/addresses/{addressId}
POST /api/v1/public/v1/account/company-change-requestsNutzen Sie account/products/purchased für den Bereich "Gekaufte Produkte". Der Server leitet diese Liste aus Bestellungen ab und speichert dafür keinen zweiten Historienbestand.
Ohne Firmenkontext liefert der Endpunkt nur persönliche Bestellungen der aktuellen Person, bei denen keine companyId gesetzt ist. Mit dem konfigurierten Firmenkontext-Header liefert er ausschließlich Bestellungen dieser Firma. Der Account braucht dafür die Firmenrolle viewer, buyer oder admin.
Produktlisten bilden die Merkliste und B2B-Standardartikel ab. Persönliche Listen gehören der aktuellen Person. Firmenlisten gehören zur ausgewählten Firma:
viewer,buyerundadmindürfen Firmenlisten lesen.- Nur
admindarf Firmenlisten erstellen, ändern oder löschen. - Persönliche Listen darf nur der Besitzer lesen und bearbeiten.
Listeneinträge speichern operative Kundendaten wie eigene Artikelnummer, Notiz, Standardmenge, eigenen Bestand und Zielbestand. Speichern Sie aktuelle Preise und Verfügbarkeit nicht lokal in der Liste. Laden Sie diese Werte bei Bedarf über den Pricing-Endpunkt:
POST /api/v1/public/v1/pricing/resolveNicht mehr öffentliche Produkte bleiben in gekauften Produkten und Listen auffindbar. Die API liefert dann productState=unavailable und nur gespeicherte Bestell- oder Public-Snapshots. Leiten Sie keine Daten aus internen PIM-, Commerce- oder Admin-Endpunkten ab.
Bereich "Meine Produkte" bauen
Bauen Sie den Kundenkonto-Bereich als Arbeitsansicht, nicht als Marketingseite. Eine robuste Struktur ist:
- Gekauft: automatisch aus Bestellungen abgeleitete Produkte.
- Listen: persönliche Listen und, bei aktivem Firmenkontext, Firmenlisten.
- Listendetail: Tabelle mit gespeicherten Mengen, eigener Artikelnummer, Notiz, eigenem Bestand, Zielbestand und Warenkorb-Aktion.
Laden Sie zuerst den Account-Kontext:
GET /api/v1/public/v1/account/companiesWenn der Nutzer eine Firma auswählt, setzen Sie den Firmenkontext im Shop und senden Sie danach bei Account-Produktlisten den konfigurierten Firmenkontext-Header:
<company-context-header>: <companyId>Laden Sie danach die gekauften Produkte:
GET /api/v1/public/v1/account/products/purchasedNutzen Sie die Antwort für eine tabellarische Ansicht. Wichtige Felder sind:
scope:personalodercompanycompanyId: gesetzt bei FirmenkontextvariantIdpricingTargetTypeunitOfMeasureskunameproductState:publicoderunavailablelastOrderedAtlastQuantitytotalQuantityorderCount
Laden Sie Produktlisten separat:
GET /api/v1/public/v1/account/product-listsDie Antwort liefert pro Liste visibility, type, name, itemsCount und canEdit. Blenden Sie Bearbeiten-Aktionen nur ein, wenn canEdit=true ist. Bei canEdit=false kann der Nutzer die Liste lesen, aber nicht ändern.
Listen anlegen und pflegen
Erstellen Sie eine persönliche Liste ohne Firmenkontext:
POST /api/v1/public/v1/account/product-lists{
"name": "Essen",
"type": "wishlist"
}Erstellen Sie eine Firmenliste mit Firmenkontext:
POST /api/v1/public/v1/account/product-lists
<company-context-header>: <companyId>{
"name": "Standardware",
"type": "standard",
"visibility": "company"
}Nutzen Sie den Listennamen für operative Gruppierungen wie Getränke, Essen, Werkstatt, Filiale München oder Projekt Müller. Dafür brauchen Sie keine Produktkategorien und keine eigenen Produktattribute.
Fügen Sie ein Produkt zu einer Liste hinzu:
POST /api/v1/public/v1/account/product-lists/{listId}/items{
"variantId": "uuid",
"pricingTargetType": "variant",
"unitOfMeasure": "PC",
"defaultQuantity": "6.0000",
"customerStockQuantity": "12.0000",
"targetStockQuantity": "24.0000",
"customerSku": "KUNDE-4711",
"note": "Standard für Standort München",
"sortOrder": 10
}Ändern Sie operative Daten eines Listeneintrags:
PATCH /api/v1/public/v1/account/product-lists/{listId}/items/{itemId}{
"defaultQuantity": "8.0000",
"customerStockQuantity": "5.0000",
"targetStockQuantity": "20.0000",
"note": "Nur für Servicefahrzeuge verwenden"
}Die API speichert Listeneinträge pro Liste. Dasselbe Produkt darf deshalb in mehreren Listen vorkommen. Innerhalb derselben Liste aktualisiert POST /items den vorhandenen aktiven Eintrag für dieselbe Variante, Pricing-Zielart und Einheit.
Preise, Verfügbarkeit und Warenkorb
Laden Sie Preise und Verfügbarkeit live, wenn Sie Listen anzeigen oder Mengen ändern:
POST /api/v1/public/v1/pricing/resolve{
"items": [
{
"variantId": "uuid",
"pricingTargetType": "variant",
"quantity": "6.0000",
"unitOfMeasure": "PC"
}
]
}Legen Sie ein Produkt aus einer Liste über den normalen Cart-Vertrag in den Warenkorb:
POST /api/v1/public/v1/cart/items{
"variantId": "uuid",
"pricingTargetType": "variant",
"quantity": "6.0000",
"unitOfMeasure": "PC"
}Der Warenkorb prüft Preis, Verfügbarkeit, Steuer, Versand und Checkout erneut. Die Produktliste gibt dafür keine Freigabe und keinen Preis-Snapshot.
Layout-Idee
Nutzen Sie im Desktop eine dichte Tabellenansicht:
| Spalte | Quelle |
|---|---|
| Artikel | name, sku, productState |
| Kundenartikelnummer | customerSku |
| Letzter Kauf | lastOrderedAt aus gekauften Produkten |
| Bestellmenge | lastQuantity oder defaultQuantity |
| Eigener Bestand | customerStockQuantity |
| Zielbestand | targetStockQuantity |
| Preis / Verfügbarkeit | pricing/resolve |
| Aktion | Warenkorb, Merken, Entfernen |
Auf Mobilgeräten eignet sich eine kompakte Listenzeile pro Artikel:
- erste Zeile: Artikelname und Verfügbarkeitsstatus
- zweite Zeile: SKU und Kundenartikelnummer
- dritte Zeile: Menge, eigener Bestand, Zielbestand
- Aktionen: Menge ändern, in den Warenkorb legen, aus Liste entfernen
Zeigen Sie leere Zustände handlungsorientiert:
- Gekauft: "Noch keine gekauften Produkte" mit Link zur Bestellhistorie oder zum Katalog.
- Listen: "Noch keine Liste" mit Aktion "Liste anlegen".
- Liste ohne Einträge: "Diese Liste enthält noch keine Produkte" mit Suche oder Kataloglink.
Behandeln Sie productState=unavailable als eigenen Zustand. Zeigen Sie den gespeicherten Namen und die SKU, aber deaktivieren Sie direkte Warenkorb-Aktion oder führen Sie den Nutzer zu einer Anfrage beziehungsweise Ersatzartikelsuche.
Nachbestellung erzeugt immer einen neuen Warenkorb. Der Server übernimmt keine historischen Preise, Rabatte oder Checkout-Entscheidungen, sondern berechnet Preis, Steuer, Verfügbarkeit und Eligibility neu.
Eigene Kontakt- und Adressendpunkte ändern nur die mit der Identity verknüpfte Person. Sie ändern keine Company und keine CompanyAddress. Firmenänderungen laufen über company-change-requests und benötigen die Rolle admin.
USt-ID und Reverse Charge
USt-ID, Kundennummer und Firmenname sind Eingaben für Review und Firmenkontext. Der Browser entscheidet nicht über Reverse Charge, Steuerfreiheit, B2B-Preise oder Rechnung.
Für EU-B2B gilt:
- Steuerlogik kommt aus dem Serverkontext.
- USt-ID-Status muss serverseitig gültig oder akzeptiert sein, bevor eine abweichende Steuerlogik gilt.
- Nicht verfügbare oder unbekannte Prüfung führt nicht automatisch zu Steuerfreiheit.
- Der Client zeigt nur die serverseitig berechneten Steuer- und Gesamtwerte.
Sicherheitsregeln
- Kein automatisches Matching auf Firmenname allein.
- Kein automatisches Matching auf E-Mail-Domain allein.
- Kein Preislistenwechsel ohne bestätigten Firmenkontext.
- Kein Rechnungskauf ohne Serverfreigabe.
- Keine CRM-Trefferlisten in öffentlichen Antworten anzeigen.
- Keine gemeinsamen Firmenlogins verwenden.
- Firmen-Rechnungsadresse nicht mit privaten oder fremden Lieferadressen mischen.
- Keine internen Prüfgründe oder Rohdaten in Fehlermeldungen ausgeben.