Warenkorb-Logik programmatisch steuern statt Model-Hacks
Wer Headless-Checkouts, POS-Integrationen oder Marketplace-Sync fur Magento 2 baut, kommt an der Quote-Management-API nicht vorbei. CartRepositoryInterface, CartManagementInterface und eigene Totals-Collector ersetzen fragilen Model-Direktzugriff durch stabile, versionierte Service Contracts, die auch bei hoher Warenkorb-Last zuverlassig bleiben.
Inhaltsverzeichnis
- 1. Was die Quote-Management-API leistet und wann man sie programmatisch braucht
- 2. CartRepositoryInterface und CartInterface im Detail
- 3. Programmatischer Warenkorb-Aufbau: Items, Adressen, Versandart
- 4. Custom Totals Collector: eigene Preislogik einhangen
- 5. GraphQL Cart Mutations vs. REST Quote-Endpoints im Vergleich
- 6. Guest-Cart zu Customer-Cart Merge-Logik steuern
- 7. Quote-Validierung per Plugin auf QuoteValidator
- 8. Asynchrones Quote-Handling in Multi-Channel-Szenarien
- 9. Performance und Caching-Fallstricke bei hoher Warenkorb-Last
- 10. Zusammenfassung
- 11. FAQ
1. Was die Quote-Management-API leistet und wann man sie programmatisch braucht
Ein Magento-Warenkorb ist intern eine Quote-Entitat: eine Vorstufe der spateren Bestellung, bestehend aus Items, Adressen, Versand- und Zahlungsdaten sowie berechneten Totals. Die Quote-Management-API ist die Sammlung von Service Contracts, uber die diese Entitat kontrolliert erzeugt, gelesen, verandert und gespeichert wird: CartRepositoryInterface, CartManagementInterface, GuestCartManagementInterface und die zugehorigen Data-Interfaces. Sie ist damit keine reine Implementierungsdetail-Frage, sondern die Grundlage jeder Integration, die einen Warenkorb ausserhalb des klassischen Storefront-Session-Flows steuern muss.
Programmatischen Zugriff braucht man in mehreren konkreten Szenarien: Bei Headless-Checkouts, in denen ein eigenes Frontend uber GraphQL oder REST direkt gegen die Quote-Management-API spricht, ohne jemals eine klassische Magento-Session zu besitzen. Bei POS-Integrationen, die Verkaufe aus physischen Kassensystemen als Warenkorbe in Magento abbilden mussen, teils Minuten nach dem eigentlichen Verkauf. Bei Marketplace-Sync, wenn Bestellungen aus externen Kanalen als Quote vorbereitet und erst danach in eine Order uberfuhrt werden. Und bei individuellen B2B-Ordering-Flows wie Schnellbestellung oder Requisition Lists, die den Standard-Warenkorb bewusst umgehen.
In all diesen Fallen ist der direkte Zugriff auf \Magento\Quote\Model\Quote zwar technisch moglich, aber riskant: Model-Klassen sind nicht Teil der stabilen API-Flache und konnen sich zwischen Minor-Releases andern. Die Quote-Management-API dagegen ist als Service Contract versioniert und geschutzt, was sie zur einzig verlasslichen Grundlage fur produktive Integrationen macht.
2. CartRepositoryInterface und CartInterface im Detail: Service Contracts statt Model-Direktzugriff
CartRepositoryInterface ist der zentrale Einstiegspunkt der Quote-Management-API fur Persistenz: get() ladt eine Quote per ID, getActive() nur aktive Warenkorbe, getForCustomer() die Quote eines eingeloggten Kunden, save() persistiert Anderungen und delete() entfernt eine Quote vollstandig. Intern delegiert das Repository an \Magento\Quote\Model\QuoteRepository, das wiederum uber die deklarativ definierten Tabellen quote, quote_item und quote_address arbeitet. Wer stattdessen direkt gegen das Model schreibt, verliert diese Abstraktionsschicht und riskiert inkonsistente Zustande.
Das dazugehorige Data-Interface CartInterface beschreibt die Quote als reines Datenobjekt: getId(), getItems(), getBillingAddress(), getShippingAddress(), getPayment(), setCustomerId() und weitere Getter/Setter, die unabhangig von der konkreten ORM-Implementierung funktionieren. Dieser Vertrag erlaubt es, Plugins und Erweiterungen zu schreiben, die auf der Schnittstelle statt auf der Model-Klasse aufsetzen, was Tests deutlich vereinfacht: In Unit-Tests lasst sich CartRepositoryInterface problemlos mocken, ohne eine echte Datenbankverbindung zu simulieren.
Ein wichtiges Detail beim Arbeiten mit der Quote-Management-API: CartRepositoryInterface::get() wirft eine NoSuchEntityException, wenn die Quote-ID nicht existiert oder nicht mehr aktiv ist. Wer diese Exception nicht explizit behandelt, produziert in Integrationen schwer nachvollziehbare 404-artige Fehler. Die konsequente Nutzung von Service Contracts statt Model-Direktzugriff ist deshalb kein Stilideal, sondern eine harte Voraussetzung fur belastbare Integrationen.
3. Programmatischer Warenkorb-Aufbau: Items hinzufugen, Adressen setzen, Versandart wahlen
Der typische Ablauf eines programmatischen Warenkorb-Aufbaus uber die Quote-Management-API folgt einem festen Muster: Zuerst wird uber CartManagementInterface::createEmptyCart() eine leere Quote angelegt, deren ID zuruckgegeben wird. Anschliessend ladt man die Quote per CartRepositoryInterface::get(), um darauf addProduct() fur jedes gewunschte Item aufzurufen. Diese Methode existiert direkt auf dem Quote-Objekt und ubernimmt Preisberechnung, Bestandsprufung und Stock-Item-Zuordnung automatisch.
Fur Versand und Zahlung mussen anschliessend Adressen gesetzt werden: getShippingAddress() liefert das Adressobjekt der Quote, das mit addData() befullt wird. Mit setCollectShippingRates(true) und collectShippingRates() werden verfugbare Versandarten ermittelt, aus denen die passende per setShippingMethod() ausgewahlt wird. Erst der abschliessende Aufruf von collectTotals() und save() uber das Repository macht den Warenkorb inklusive aller Totals persistent.
Das folgende Beispiel zeigt eine vollstandige Service-Klasse mit Constructor Property Promotion, die genau diesen Ablauf kapselt und dabei ausschliesslich Service Contracts der Quote-Management-API nutzt:
<?php
declare(strict_types=1);
namespace Mironsoft\QuoteApi\Service;
use Magento\Quote\Api\CartManagementInterface;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Quote\Model\Quote\Address\Rate;
/**
* Builds a Magento quote programmatically without touching storefront session state.
*/
class ProgrammaticQuoteBuilder
{
public function __construct(
private readonly CartManagementInterface $cartManagement,
private readonly CartRepositoryInterface $cartRepository,
private readonly ProductRepositoryInterface $productRepository,
private readonly StoreManagerInterface $storeManager,
) {
}
/**
* Creates a new quote, adds items, sets shipping address and selects a shipping method.
*
* @param array<string, int> $skuToQty
* @param array<string, string> $shippingAddressData
* @return CartInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function build(array $skuToQty, array $shippingAddressData): CartInterface
{
$storeId = (int) $this->storeManager->getStore()->getId();
$quoteId = $this->cartManagement->createEmptyCart();
$quote = $this->cartRepository->get($quoteId);
$quote->setStoreId($storeId);
foreach ($skuToQty as $sku => $qty) {
$product = $this->productRepository->get((string) $sku, false, $storeId);
$quote->addProduct($product, (int) $qty);
}
$shippingAddress = $quote->getShippingAddress();
$shippingAddress->addData($shippingAddressData);
$shippingAddress->setCollectShippingRates(true);
$shippingAddress->collectShippingRates();
$rates = $shippingAddress->getGroupedAllShippingRates();
foreach ($rates as $carrierRates) {
foreach ($carrierRates as $rate) {
/** @var Rate $rate */
$shippingAddress->setShippingMethod($rate->getCode());
break 2;
}
}
$quote->setTotalsCollectedFlag(false);
$quote->collectTotals();
return $this->cartRepository->save($quote);
}
}
4. Custom Totals Collector: eigene Preislogik per TotalSegmentInterface in die Quote einhangen
Die Totals-Berechnung der Quote-Management-API lauft uber eine Pipeline von Collector-Klassen, die per di.xml unter Magento\Quote\Model\Quote\TotalsCollector mit einer sortOrder registriert werden. Jeder Collector erweitert \Magento\Quote\Model\Quote\Address\Total\AbstractTotal und implementiert zwei Methoden: collect() berechnet den Wert und schreibt ihn in das Total-Objekt der Adresse, fetch() gibt ihn als Segment fur die API-Antwort zuruck, konzeptionell kompatibel mit \Magento\Quote\Api\Data\TotalSegmentInterface.
Ein typischer Anwendungsfall: Eine Handling-Gebuhr, die abhangig vom Gesamtgewicht der Bestellung berechnet wird und in REST- wie GraphQL-Antworten als eigener Totals-Eintrag sichtbar sein muss, ausserhalb der Standard-Steuer- und Rabattlogik. Ohne eigenen Collector musste diese Logik entweder in einem Produktpreis versteckt oder nachtraglich in der Order manipuliert werden, beides fehleranfallig und schwer nachvollziehbar in Reports.
Die folgende Klasse implementiert genau diesen Fall als eigenstandigen Totals-Collector innerhalb der Quote-Management-API-Pipeline:
<?php
declare(strict_types=1);
namespace Mironsoft\QuoteApi\Model\Total;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\Quote\Address\Total;
use Magento\Quote\Model\Quote\Address\Total\AbstractTotal;
use Magento\Quote\Api\Data\ShippingAssignmentInterface;
/**
* Adds a custom "handling fee" total segment to the quote based on item weight.
*/
class HandlingFeeTotal extends AbstractTotal
{
private const CODE = 'handling_fee';
private const WEIGHT_THRESHOLD_KG = 20.0;
private const FEE_AMOUNT = 4.90;
/**
* Sets the total segment code used in totals responses.
*/
public function __construct()
{
$this->setCode(self::CODE);
}
/**
* Collects the handling fee and writes it into the address total.
*
* @param Quote $quote
* @param ShippingAssignmentInterface $shippingAssignment
* @param Total $total
* @return $this
*/
public function collect(
Quote $quote,
ShippingAssignmentInterface $shippingAssignment,
Total $total
): self {
parent::collect($quote, $shippingAssignment, $total);
$items = $shippingAssignment->getItems();
if (!$items) {
return $this;
}
$totalWeight = 0.0;
foreach ($items as $item) {
$totalWeight += (float) $item->getWeight() * (float) $item->getQty();
}
$fee = $totalWeight > self::WEIGHT_THRESHOLD_KG ? self::FEE_AMOUNT : 0.0;
$total->setTotalAmount(self::CODE, $fee);
$total->setBaseTotalAmount(self::CODE, $fee);
$total->setHandlingFee($fee);
return $this;
}
/**
* Exposes the segment as a total segment array for API responses.
*
* @param Quote $quote
* @param Total $total
* @return array<string, mixed>|null
*/
public function fetch(Quote $quote, Total $total): ?array
{
$fee = (float) $total->getHandlingFee();
if ($fee <= 0.0) {
return null;
}
return [
'code' => self::CODE,
'title' => __('Handling Fee'),
'value' => $fee,
];
}
}
5. GraphQL Cart Mutations (addSimpleProductsToCart, setShippingMethodsOnCart) vs. REST Quote-Endpoints im Vergleich
Die Quote-Management-API ist uber zwei parallele API-Layer erreichbar. GraphQL Cart Mutations wie addSimpleProductsToCart, setShippingMethodsOnCart oder setBillingAddressOnCart arbeiten mit einer maskierten cart_id, die in der Tabelle quote_id_mask auf die interne Quote-ID abgebildet wird. Der Client halt nur diesen Token, nie die echte ID, was eine saubere Trennung zwischen offentlichem und internem Identifier erzwingt und Enumeration-Angriffe erschwert.
REST-Endpunkte wie /V1/carts/mine fur eingeloggte Kunden oder /V1/guest-carts/:cartId fur Gaste bilden dieselbe Quote-Management-API ab, unterscheiden sich aber im Auth-Modell: REST setzt entweder einen Customer-Token oder eine offentliche Guest-Cart-ID voraus, wahrend GraphQL zusatzlich Multi-Operation-Queries in einem einzigen Request erlaubt, was bei mehreren Warenkorb-Anderungen pro Seitenaufruf spurbar Latenz spart. Direkter Service-Contract-Zugriff aus eigenem PHP-Code umgeht beide HTTP-Layer vollstandig und ist dadurch am schnellsten, aber auch am starksten an den Magento-Prozess gebunden.
Die folgende Tabelle stellt alle drei Zugriffswege der Quote-Management-API gegenuber, bevor die konkreten Code-Beispiele folgen:
| Zugriffsweg | Typischer Use Case | Latenz | Statelessness | Auth-Modell |
|---|---|---|---|---|
| REST Quote API | Einfache Integrationen, POS-Anbindung | Mittel, ein Request pro Operation | Vollstandig stateless | Customer-Token / Guest-Cart-ID |
| GraphQL Cart Mutations | Headless Storefronts, PWA-Checkouts | Niedrig, gebundelte Operationen | Vollstandig stateless | Customer-Token / maskierte Cart-ID |
| Direkter Service Contract | Consumer, Admin-Tools, interne Jobs | Minimal, kein HTTP-Overhead | An Prozess gebunden | Prozessintern, kein Token |
Ein GraphQL Cart Mutation Beispiel zeigt, wie zwei Operationen der Quote-Management-API gebundelt werden konnen, sobald eine Cart-ID vorliegt:
mutation AddSimpleProductAndSetShipping($cartId: String!, $sku: String!, $qty: Float!) {
addSimpleProductsToCart(
input: {
cart_id: $cartId
cart_items: [{ data: { sku: $sku, quantity: $qty } }]
}
) {
cart {
id
items {
quantity
product { sku }
}
}
}
}
mutation SetShipping($cartId: String!, $carrierCode: String!, $methodCode: String!) {
setShippingMethodsOnCart(
input: {
cart_id: $cartId
shipping_methods: [{ carrier_code: $carrierCode, method_code: $methodCode }]
}
) {
cart {
shipping_addresses {
selected_shipping_method {
carrier_code
method_code
}
}
}
}
}
Der gleichwertige REST-Zugriff auf die Quote-Management-API zeigt denselben Ablauf uber zwei separate Requests:
# Create a guest cart via the REST Quote API
curl -s -X POST "https://shop.example.com/rest/V1/guest-carts" \
-H "Content-Type: application/json"
# Response: "b8f3b3b1b1b1b1b1b1b1b1b1b1b1b1b1"
# Add an item to the guest cart
curl -s -X POST "https://shop.example.com/rest/V1/guest-carts/b8f3b3b1.../items" \
-H "Content-Type: application/json" \
-d '{
"cartItem": {
"sku": "24-MB01",
"qty": 2,
"quote_id": "b8f3b3b1b1b1b1b1b1b1b1b1b1b1b1b1"
}
}'
# Response contains item_id, price, qty and quote_id
6. Guest-Cart zu Customer-Cart Merge-Logik verstehen und per Plugin steuern
Meldet sich ein Gast wahrend eines aktiven Warenkorbs an, greift innerhalb der Quote-Management-API CartManagementInterface::assignCustomer(). Standardmassig versucht Magento, die Gast-Quote mit einer bereits existierenden aktiven Kunden-Quote zusammenzufuhren, abhangig von den Einstellungen unter Sales > Checkout > Shopping Cart. Dieses Verhalten ist fur klassische Storefronts sinnvoll, kann in speziellen Integrationsszenarien aber zu unerwunschten Effekten fuhren.
Ein Beispiel: Bei Marketplace-Accounts, die uber mehrere Kanale gleichzeitig aktiv sind, soll die Gast-Quote eines POS-Verkaufs nicht automatisch mit der Online-Quote desselben Kunden verschmolzen werden. Hier registriert man ein Plugin um assignCustomer(), das entweder den Merge unterdruckt oder die Entscheidung an eine eigene Regel delegiert, etwa basierend auf einem Custom-Attribut an der Quote, das den Ursprungskanal markiert.
Zwei Fallstricke sind bei dieser Anpassung der Quote-Management-API zu beachten: Erstens konnen ohne sauberes Merge-Handling verwaiste Quotes mit is_active = 0 entstehen, die Speicherplatz belegen und in Reports storen. Zweitens fuhrt gleichzeitiges Aufrufen von assignCustomer() aus zwei parallelen Requests, etwa bei Login uber mehrere Tabs, potenziell zu einer Race Condition, bei der beide Requests denselben Merge-Vorgang unabhangig ausfuhren.
7. Quote-Validierung: eigene Validatoren per Plugin auf QuoteValidator einhangen
\Magento\Quote\Model\QuoteValidator::validateQuote() ist die zentrale Prufstelle der Quote-Management-API vor dem Checkout-Abschluss. Standardmassig wird gepruft, ob die Quote uberhaupt Items enthalt, ob sie aktiv ist und ob alle Items noch kaufbar sind. Diese Prufung lauft sowohl im klassischen Onepage-Checkout als auch beim programmatischen Order-Placement uber CartManagementInterface::placeOrder().
Fur zusatzliche Geschaftslogik registriert man ein Plugin auf QuoteValidator, etwa um zu prufen, ob alle Items in einem bestimmten POS-Lager tatsachlich vorratig sind, oder ob eine per Marketplace-Sync erzeugte Quote nicht alter als eine definierte Zeitspanne ist, bevor sie in eine Order uberfuhrt wird. Ein around-Plugin kann die Standardprufung erweitern, ohne sie zu ersetzen, ein after-Plugin reicht fur reine Zusatzchecks meist aus.
Die Registrierung eines solchen Plugins erfolgt uber di.xml, wie im folgenden Beispiel fur einen Frische-Check auf Marketplace-Quotes:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\QuoteValidator">
<plugin name="mironsoft_quote_freshness_validator"
type="Mironsoft\QuoteApi\Plugin\Model\QuoteFreshnessValidatorPlugin"
sortOrder="10"/>
</type>
</config>
8. Asynchrones Quote-Handling in Multi-Channel-Szenarien (Message Queue, POS-Sync, Idempotenz)
In Multi-Channel-Setups laufen viele Anfragen an die Quote-Management-API nicht synchron im Request-Response-Zyklus, sondern uber eine Message Queue. Ein typisches Muster: POS-Kassen publizieren Verkaufsereignisse an ein RabbitMQ-Topic, ein Consumer verarbeitet diese Nachrichten und erzeugt oder aktualisiert Quotes uber dieselben Service Contracts wie im synchronen Fall, nur eben entkoppelt von der Verfugbarkeit des POS-Systems.
Entscheidend ist dabei Idempotenz: Externe Systeme wiederholen Nachrichten bei Zweifel am Zustellungserfolg, was ohne Schutzmechanismus zu doppelt angelegten Quotes fuhrt. Die ubliche Losung ist ein Idempotency-Key, der als Custom-Attribut an der Quote oder in einer separaten Zuordnungstabelle gespeichert wird, bevor die eigentliche Quote-Management-API-Operation ausgefuhrt wird. Erhalt der Consumer eine Nachricht mit bereits bekanntem Key, wird die Verarbeitung uberspringen statt eine zweite Quote anzulegen.
Technisch wird ein solcher Consumer uber queue_topology.xml, communication.xml und queue_consumer.xml registriert. Die eigentliche Verarbeitungslogik im Consumer unterscheidet sich dabei kaum von der synchronen Variante aus Abschnitt 3: dieselben Interfaces, derselbe Aufbau, nur ein anderer Trigger. Das ist einer der grossten Vorteile der Quote-Management-API als Service-Contract-Schicht: Sie ist transportunabhangig und funktioniert identisch, ob synchron per HTTP oder asynchron per Queue aufgerufen.
9. Performance und Caching-Fallstricke bei hoher Warenkorb-Last (quote_id_mask, session handling, race conditions)
Die Tabelle quote_id_mask bildet offentliche, maskierte Cart-IDs auf interne Quote-IDs ab und wird bei jeder GraphQL- oder REST-Operation der Quote-Management-API gelesen. Bei hoher gleichzeitiger Warenkorb-Last, etwa wahrend einer Kampagne mit vielen parallelen Guest-Checkouts, wird diese Tabelle zum Hotspot. Fehlende oder ungunstig gesetzte Indizes fuhren dann zu spurbaren Latenzspitzen, gerade weil jede Mutation zunachst diesen Lookup durchlauft, bevor die eigentliche Quote geladen wird.
Ein zweiter Fallstrick betrifft die Kopplung von PHP-Session und aktiver Quote-ID im klassischen Storefront-Flow. Wer die Quote-Management-API direkt nutzt, umgeht diese Kopplung bewusst, was fur Skalierbarkeit gunstig ist, aber explizite Absicherung gegen parallele Schreibzugriffe erfordert: Zwei gleichzeitige Requests, die beide collectTotals() und save() auf derselben Quote ausfuhren, konnen sich gegenseitig uberschreiben und zu verlorenen Updates fuhren.
Als Absicherung empfiehlt sich \Magento\Framework\Lock\LockManagerInterface fur kritische Schreiboperationen auf einer Quote, insbesondere in asynchronen Consumer-Szenarien aus Abschnitt 8. Die Quote-Entitat selbst sollte dabei nie als Ganzes gecacht werden, nur berechnete, rein lesende Darstellungen wie Totals-Anzeigen sind fur Full-Page-Caching geeignet. Wer diese Grenze verwischt, riskiert veraltete Preise oder Versandarten in einer scheinbar aktuellen Warenkorb-Ansicht.
10. Zusammenfassung
Die Quote-Management-API in Magento 2 ist die einzig verlassliche Grundlage fur jede Integration, die einen Warenkorb ausserhalb des klassischen Checkout-Flows steuern muss. CartRepositoryInterface und CartManagementInterface ersetzen fragilen Model-Direktzugriff durch versionierte Service Contracts. Programmatischer Warenkorb-Aufbau, eigene Totals-Collector, GraphQL Cart Mutations und REST-Endpunkte greifen alle auf dieselbe zugrunde liegende Schicht zu, egal ob synchron oder uber Message Queue.
Wer POS-Sync, Marketplace-Integrationen oder Headless-Checkouts baut, sollte von Anfang an konsequent auf diese Service Contracts setzen, eigene Validatoren sauber per Plugin einhangen und bei hoher Last besonders auf quote_id_mask-Performance sowie Race Conditions bei parallelen Schreibzugriffen achten. Die Quote-Management-API zahlt sich genau dann aus, wenn mehrere Kanale gleichzeitig auf denselben Warenkorb-Zustand zugreifen mussen.
Quote-Management-API in Magento 2: Das Wichtigste auf einen Blick
Service Contracts
CartRepositoryInterface und CartManagementInterface statt Model-Direktzugriff. Stabile, versionierte Grundlage fur jede Warenkorb-Integration.
Custom Totals
Eigene Preislogik uber Totals-Collector einhangen, statt Preise in Produktdaten oder Order-Manipulation zu verstecken.
GraphQL & REST
GraphQL Cart Mutations fur gebundelte Headless-Operationen, REST fur einfache Integrationen wie POS-Anbindung.
Performance & Idempotenz
quote_id_mask im Blick behalten, Idempotency-Keys fur asynchrone Sync-Prozesse, Locks statt Cache fur schreibende Zugriffe.
11. FAQ: Quote-Management-API in Magento 2
1Was ist die Quote-Management-API in Magento 2?
2Wann CartRepositoryInterface statt Model direkt?
3Wie fuge ich Produkte programmatisch hinzu?
4Eigene Preislogik in Quote-Totals einhangen?
5GraphQL Cart Mutations vs. REST?
6Wie funktioniert der Cart-Merge beim Login?
7Eigene Warenkorb-Validierung einbauen?
8POS-Warenkorbe asynchron synchronisieren?
9Was ist quote_id_mask?
10Race Conditions bei parallelen Updates vermeiden?
Mironsoft
Magento-2-Entwicklung, Service Contracts und API-Integrationen
Eigene Quote-Management-API-Integration fur euren Shop gesucht?
Wir bauen Headless-Checkouts, POS-Anbindungen und Marketplace-Sync auf Basis der Quote-Management-API, mit sauberen Service Contracts, eigenen Totals-Collectors und robuster Plugin-Validierung fur euren Magento-2-Shop.
API-Architektur
CartRepositoryInterface, Custom Totals und Service Contracts sauber geplant und implementiert
Headless & GraphQL
Cart Mutations, PWA-Checkouts und mobile Storefronts an die Quote-Management-API angebunden
POS & Marketplace
Asynchrone Warenkorb-Synchronisation mit Idempotenz und sauberer Merge-Logik