← Back to blog

Magento 2 Marketplace Integration: Listings, Orders, Stock

Rodrigo Biazzi

Selling the same catalog on Amazon, eBay, Mercado Livre, Shopee or any other marketplace while Magento stays the main store sounds like a sync problem. In practice it is mostly a data modeling problem. Each marketplace has its own category tree, its own required attributes per category, its own variation model and its own rules for titles, images and prices. Moving data is the easy part. Mapping it so listings are accepted and stay accepted is where the work goes.

This article covers the architecture choices for connecting Magento 2 to marketplaces, the data you need to model, how to bring marketplace orders back into Magento, and the stock rules that keep you from overselling.

Three ways to connect

Connector extension inside Magento

A module installed in Magento that talks to the marketplace APIs directly. Listings, stock updates and order imports run as cron jobs or queue consumers inside the store. It is quick to start and keeps everything in one admin. The downsides are that marketplace API changes force extension updates on your release cycle, heavy sync jobs run on the same servers as the storefront, and the quality of these extensions varies a lot. Review the code of one before you install it.

Hub or ERP in the middle

Many ERPs and multichannel tools already integrate with the major marketplaces. In this setup the ERP is the hub: it publishes listings, receives marketplace orders and sends stock to every channel, including Magento. Magento becomes one channel among several. This is often the most stable option when the ERP handles invoicing and fulfillment anyway, because marketplace orders never need to enter Magento at all.

Custom middleware

A separate service that reads the catalog from Magento, transforms it per marketplace, publishes listings and pushes orders to wherever they need to go. It gives full control and keeps the store clean, but you own every API change on every marketplace. It makes sense when the catalog has rules no off the shelf tool handles, or when volume justifies the investment.

The data you have to model

Categories

Marketplace categories rarely match your store tree. You need a mapping from each Magento category, or from a product attribute, to a marketplace category ID. The mapping should live in data, not in code, because marketplaces change their trees. A product attribute such as marketplace_category_id per channel, or a mapping table keyed by your own category, both work. A product attribute is simpler when products sit in several store categories, since you avoid the question of which category wins.

Required attributes per category

This is the part that causes most rejected listings. A marketplace category may require brand, model, material, voltage, gender or any of dozens of fields, each with a fixed list of accepted values. Magento attributes are usually free text or have options worded for shoppers, not for the marketplace validator.

Two practical rules help here:

  • Pull the list of required attributes and allowed values from the marketplace API per category, and store it. Validate products against it before sending, so you see errors in your system instead of in a rejection email.
  • Map option values explicitly. A Magento option "Stainless steel" may have to become a specific value ID on the marketplace. Keep that mapping in a table you can edit without a deployment.

Good attribute design in Magento makes all of this easier. See product attributes best practices for how to set up select attributes so they map cleanly.

Variations

Magento configurable products map to parent and child listings on most marketplaces, but the variation axes must be ones the marketplace accepts for that category. If your configurable uses an attribute the marketplace does not recognize as a variation, you either map it to an accepted one or list each child as its own product. Decide this per category before you build the sync.

Titles, descriptions and images

Marketplaces have length limits on titles, rules against HTML or certain words in descriptions, and image rules about background and size. Keep marketplace specific titles and descriptions in separate attributes instead of transforming the store copy on the fly. Strip HTML from descriptions before sending, since many marketplaces reject or flag markup.

Prices

Marketplace prices usually differ from the store price because of commissions and fees. Store them as separate attributes per channel, or compute them from the base price with a rule you can explain. Never let the connector derive them silently from a formula buried in code.

Importing marketplace orders into Magento

If marketplace orders have to live in Magento, for example because Magento triggers the ERP export and invoicing, you need to create orders programmatically. The marketplace already charged the buyer, so the order needs a payment method that does nothing at capture, and a shipping method that records what the marketplace chose. Both are small custom modules, as shown in a custom payment method.

A minimal service that builds a quote and submits it:

<?php
declare(strict_types=1);

namespace Vendor\Marketplace\Model;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Model\QuoteFactory;
use Magento\Quote\Model\QuoteManagement;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Store\Model\StoreManagerInterface;

class OrderCreator
{
    public function __construct(
        private readonly QuoteFactory $quoteFactory,
        private readonly CartRepositoryInterface $cartRepository,
        private readonly QuoteManagement $quoteManagement,
        private readonly ProductRepositoryInterface $productRepository,
        private readonly StoreManagerInterface $storeManager
    ) {
    }

    public function create(array $data): OrderInterface
    {
        $store = $this->storeManager->getStore($data['store_code']);

        $quote = $this->quoteFactory->create();
        $quote->setStore($store);
        $quote->setCurrency();
        $quote->setCustomerIsGuest(true);
        $quote->setCustomerEmail($data['email']);

        foreach ($data['items'] as $line) {
            $product = $this->productRepository->get($line['sku'], false, (int) $store->getId());
            $item = $quote->addProduct($product, new DataObject(['qty' => $line['qty']]));
            if (is_string($item)) {
                throw new LocalizedException(__($item));
            }
            $item->setCustomPrice($line['price']);
            $item->setOriginalCustomPrice($line['price']);
        }

        $quote->getBillingAddress()->addData($data['address']);
        $quote->getShippingAddress()
            ->addData($data['address'])
            ->setCollectShippingRates(true)
            ->collectShippingRates()
            ->setShippingMethod($data['shipping_method']);

        $quote->getPayment()->importData(['method' => $data['payment_method']]);
        $quote->collectTotals();
        $this->cartRepository->save($quote);

        $order = $this->quoteManagement->submit($quote);
        $order->setExtOrderId($data['marketplace_order_id']);

        return $order;
    }
}

The custom price keeps the marketplace price on the order even if it differs from the catalog price. Persist the marketplace order ID (the example sets ext_order_id, then save the order through the repository) and check it before creating anything, so a repeated import does not create a duplicate. Run the import in a queue consumer or cron, not in a web request, and log every failure with the marketplace order ID so someone can fix and retry it.

Also decide what to do when a SKU no longer exists or is disabled in Magento. The marketplace order is real and already paid, so the importer should not drop it. Put it in an error state with a clear reason instead.

Stock and overselling

Overselling on marketplaces costs more than on your own store, because marketplaces penalize cancellations with lower ranking or account restrictions. A few rules reduce the risk:

  1. Send salable quantity, not physical quantity. With MSI, salable quantity already subtracts reservations from orders not yet shipped. The details are in salable quantity and reservations.
  2. Keep a buffer. Send salable quantity minus a safety margin per product or category, so the time between a sale on one channel and the stock update on the others does not cause an oversell.
  3. Push changes, do not only poll. Trigger a stock update for a SKU when its salable quantity changes, and run a full reconciliation on a schedule to catch anything missed.
  4. Use a dedicated stock if needed. MSI lets you assign sources to stocks per sales channel. A separate stock for marketplaces gives you a different view of availability, as explained in MSI sources and stocks.

Monitoring

A marketplace integration needs an error screen that a non developer can read. For every listing, store the last status from the marketplace and the last error message, translated into plain language when possible. Most marketplace APIs return detailed error codes for rejected listings. Exposing them in an admin grid saves hours of developer time reading logs.

FAQ

Should marketplace orders be created in Magento?

Only if Magento drives something downstream, such as the ERP export or invoicing. If an ERP already receives marketplace orders directly, adding them to Magento creates duplicates and extra work.

Why are my listings rejected even though the product is complete in Magento?

Usually because a category specific required attribute is missing or its value is not in the list accepted by the marketplace. Validate products against the marketplace attribute list before sending.

Which quantity should I send to marketplaces?

Send salable quantity from MSI, minus a safety buffer. Physical quantity ignores orders that are placed but not shipped yet, which leads to overselling.

How do I keep marketplace order imports from creating duplicates?

Store the marketplace order ID on the Magento order and check for it before creating a new one. Make the import idempotent so retries are safe.

I am available for freelance Magento and Adobe Commerce work, including marketplace and ERP integrations. If you need help with this, get in touch.

Related articles