Marketplace Channels¶
Sell a shop's products on marketplaces and shopping channels (eBay and Google Shopping today) without keeping a second catalogue. Badger stays the system of record: products, stock and orders live here, the channel reads from them, and marketplace orders come back into Badger as ordinary orders that are packed and dispatched with everything else.
| Channel | Listings | Orders | Dispatch, cancel, refund | Payouts |
|---|---|---|---|---|
| Google Shopping (Merchant Center free listings) | Yes | No (shoppers buy on your site) | n/a | n/a |
| eBay | Yes, including variations | Imported automatically | Sent back to eBay | Fetched and reconciled |
Channels are off by default. Turn on Marketplace channels (channels-enabled, under Settings > Integrations) to show Channels in the admin menu.
Status
The eBay and Google connectors are written against the marketplaces' published APIs but have not yet been run against a live eBay or Merchant Center account. Treat the first connection on each platform as a pilot.
How it works¶
- Connect an account. eBay: choose the marketplace (eBay UK, US, ...) and sign in to eBay to grant access. Google: add Badger's service account email as a user of your Merchant Center, then enter your Merchant Center id. One site can connect several accounts.
- Choose your settings. Price rule (e.g. +10% to cover fees), how much stock to hold back for your own site, the most units to offer, handling days, whether to import orders, whether to publish automatically, and for eBay your business policies (postage, payment, returns) and where items ship from.
- Map your categories. For each taxonomy level (or collection), pick the channel category its products go in, and say how your attributes fill the channel's (e.g. your "Material" → eBay "Fabric", "Navy" → "Blue"). A single product can also have its own category.
- Add products. Pick them one by one, or add everything on a category page of your category tree, a taxonomy category (sub-categories included) or a collection. Badger checks each product against the channel: missing images, prices, required attributes (eBay item specifics), barcodes where the category needs them, and products that can't be sold there. Each listing shows what's stopping it in plain words.
- Fill the gaps with AI. For a listing that's missing attributes, Suggest with AI proposes values from the product's own title, description and attributes. Nothing is published until you accept them.
- Publish. Ready listings go out when you press Publish (or straight away with automatic publishing). After that, changes to the product are republished automatically, and stock levels are pushed every 15 minutes.
- Orders arrive. Paid eBay orders are imported every couple of minutes as marketplace orders: stock is taken, the order gets a number and appears in the fulfilment queue. eBay sends the buyer its own emails, so Badger doesn't.
- Dispatch as usual. Dispatching with a carrier and tracking number sends them to eBay. Cancelling sends a cancellation; a refund is made on eBay.
- Reconcile payouts. Each eBay payout is fetched with its sales, refunds and fees, matched to Badger orders, and marked reconciled when it adds up.
What can't be listed¶
Donations, gift cards, event tickets and subscriptions are not eligible. eBay only takes items that are posted. Disabled variants aren't offered.
Marketplace orders¶
A marketplace order is an Order with channel = MARKETPLACE and a channelRef naming the account and the marketplace's order id (unique per account, so an order is only ever imported once). Its payment is a MarketplacePaymentMethod intent, already captured: the buyer paid the marketplace, which pays the seller later in a payout.
- Buyer details are kept on the order only (name, delivery address, the email the marketplace gives). Each account has one anonymous user that owns its imported orders, so buyers aren't created as customers.
- Tax the marketplace collects (US marketplace-facilitator sales tax) is left out of the order's total and tax and recorded in
channelRef.marketplaceTaxCollected, since the seller never receives it. - Orders that can't be imported (usually an eBay SKU that isn't on the site) are held back and listed on the account, and retried on every sync until they import. Fix the SKU on the eBay listing or the product.
- Cancelled on eBay after import: the order is cancelled in Badger too (stock restored), without sending anything back to eBay.
The order pipelines:
| Pipeline | What happens for a marketplace order |
|---|---|
importChannelOrder (new) |
Set submitted, take stock, number the order, auto-fulfil digital lines, record the sale, announce fulfilment work. No payment, no confirmation email. |
dispatchOrder |
queueChannelShipmentProcessor queues the carrier and tracking for the marketplace. The dispatch email is skipped. |
cancelOrder |
queueChannelCancellationProcessor queues a cancellation (unless the cancellation came from the marketplace). The refund the cancellation makes is recorded but not sent separately. The cancellation email is skipped. |
refundOrder |
The marketplace payment method queues a refund for the marketplace. |
Updates to the marketplace go through an outbox (ChannelTask): they're written when the order changes and sent by the worker with retries (backing off up to 6 hours, 12 attempts). Ones that keep failing are listed on the account with a Retry button, so a slow marketplace never holds up dispatch in Badger.
Architecture¶
| Where | What |
|---|---|
common channels package |
Documents: ChannelAccount, ChannelListing, ChannelCategoryMapping, ChannelCategorySchema (cached, shared across sites), ChannelTask, ChannelPayout, and their repositories |
commerce-core channels package |
Services: accounts (credentials encrypted with a per-site key, ChannelTokenCipher), listings and readiness (ChannelListingPreparer), mappings, order import, outbox, payouts, AI suggestions. Marketplace-neutral: no eBay or Google code |
channels module |
ChannelConnector SPI, the eBay and Google connectors, ChannelOperations (connect, categories, policies for admin and MCP) and the sync job |
worker |
Runs the sync job (badger.channels.sync.enabled: true) |
web-admin |
The Channels screens and the eBay OAuth callback |
ai |
MCP tools and the AI attribute suggester |
The sync job leases one account at a time (claimNextDue, a findAndModify on the account), so several worker pods share accounts without syncing one twice. It runs every 30 seconds (badger.channels.sync.interval-ms) on its own thread, not the worker's shared @Scheduled pool, so a long job there (a big stock reindex) can't delay order imports. Each pass sends outbox updates, imports orders, publishes changed listings (25 per pass), pushes stock every 15 minutes, reads Google's review of live listings every 6 hours (and an hour after a publish) and fetches payouts every 6 hours. A refused credential stops the account (Needs reconnecting) until the owner reconnects; other failures back off.
Listings are flagged for republishing when their product is saved (from ProductIndexEvent), when a mapping or the account's settings change, or by the owner. A hash of the published payload stops unchanged products being re-sent; quantities don't count towards it.
From an AI agent (MCP)¶
The channels and manageChannels MCP tools do everything the Channels screens do: create and
configure accounts, map categories, add products, ask for AI attribute suggestions and accept them,
publish, and read payouts and failed updates. opsInbox counts accounts to reconnect, failed
updates, orders that couldn't import, listings in error and live listings Google has since
disapproved. An agent can't grant eBay access
itself: it gets a link for the owner to open. See MCP.
Platform setup (superadmin)¶
Global settings, group channels-config:
| Key | What |
|---|---|
channels-ebay-client-id, channels-ebay-client-secret |
The eBay developer keyset (sandbox or production) |
channels-ebay-ru-name |
The RuName whose accepted URL is https://<platform host>/channels/oauth/ebay |
channels-ebay-sandbox |
Use eBay's sandbox for every site (default on) |
channels-google-service-account |
JSON key of a Google Cloud service account with the Merchant API enabled |
Connecting checks that eBay can pay the seller (opted in to eBay managed payments for the marketplace, Account API payments_program). A seller eBay can't pay can list, but buyers can't check out, so the account doesn't connect until payments are set up in Seller Hub, and it says which steps are left. Check connection on the account runs the checks again without going back to eBay.
On the sandbox, an account's Settings page also offers Create test policies: a sandbox test seller has no business policies, so it makes a free Royal Mail postage, an eBay-managed payment and a 30-day returns policy (named "Badger sandbox test", reused if they exist) and chooses them. It's never offered against production eBay, where the seller sets their own postage and returns.
For Google, the Merchant API must be enabled on the service account's Google Cloud project, and that project registered once with developerRegistration.registerGcp against a Merchant Center where you're an admin. Shops then add the service account email as a user of their own Merchant Center (Settings > People and access) and enter their Merchant Center id; Badger creates a "Badger" API data source in it for the feed label (the account's country) and the site's language. Google reviews products after accepting them, sometimes days later. Its disapprovals and warnings are read back for the whole account every 6 hours, and an hour after a publish (products.list, a thousand products a call, so 20,000 products cost 20 calls), and replace the listing's previous review issues without a republish. Accounts with nothing live make no call, and sites without a channel account cost nothing: the worker only looks at accounts that are due.
Each Google product also carries the shop's own category paths as product types (its category in the main category tree, then its taxonomy level, e.g. "Fixings > Screws > Torx screws"), which Google uses for bidding and reports, and its taxonomy attributes as product details (a variant's own options first, such as its length). Attributes Google has a field for (colour, size, material, pattern, gender, age group, brand) go in that field instead, and one that differs between a product's variants is left off the product details.
The OAuth callback lands on the platform host, not the shop's domain. It carries a single-use state (random, hashed at rest, valid 15 minutes) that names the account, then redirects to that account's site admin.
Site data¶
Channel accounts, listings, mappings, outbox tasks and payouts are deleted with the site. They aren't included in site export: credentials are bound to the source site, and a copy would publish to the same marketplace account. The per-site credential key (channel-token-encryption-key) is a secret and is never exported.
Not built yet¶
- Amazon (SP-API), Etsy, and other marketplaces.
- Returns raised on the marketplace (handle them on eBay; refunds made there aren't imported yet).
- Repricing rules beyond a single percentage per account, and multi-currency listings.
- A REST contract for third-party connectors.