@techmarbles/medusa-odoo-connector
v0.0.7
Published
Medusa v2 ↔ Odoo connector: two-way sync of products, inventory, orders and customers, with an admin UI and tiered Stripe-billed plans.
Maintainers
Readme
medusa-odoo-connector
A Medusa v2 plugin that syncs your store with Odoo — products, inventory, orders, and customers — with a built-in admin UI and tiered (Stripe-billed) plans.
Architecture: this plugin runs inside Medusa and talks to a small companion service (odoo-api) over HTTP, which does the actual Odoo JSON-RPC — so Odoo credentials never touch the browser. By default the plugin uses a hosted odoo-api (no setup required); you can self-host it and point
ODOO_API_URLat your own instance.
Features
| Area | What it does | |------|--------------| | Products | Import (Odoo → Medusa) and export (Medusa → Odoo) with variants, options, per-variant prices, images, and title-slug handles. Idempotent; variant reconciliation on update (create new / update existing, never delete). | | Inventory | Two-way stock sync by SKU, mapped per location ↔ Odoo warehouse (on-hand / forecasted). | | Orders | Push Medusa orders to Odoo as sale orders (+ optional invoice & payment). Auto on placement or manual. Atomic fail on unmatched SKU. | | Customers | Two-way customer sync (matched by email). | | Scheduling | Background imports via BullMQ + Redis (off / 30 min / hourly / twice-daily / daily). Redis optional — without it, syncs run inline on demand (no scheduling). | | Admin UI | Setup wizard, Dashboard, and a settings page per entity, plus Sync, Logs, and Plans. | | Plans & billing | Free / Starter / Pro / Enterprise tiers that gate what syncs (product caps, sync direction, schedule frequency, entity access), with optional Stripe subscriptions. |
Requirements
- Medusa v2 (works on
2.13.x–2.15.x) - Node >= 20
- Redis — optional. Reused from Medusa's own
REDIS_URL; without it the connector falls back to inline syncs (see Redis & background processing) - An odoo-api service to talk to — a hosted default is built in; self-hosting is optional (see below)
- An Odoo instance with API access (URL, database, username, API key)
Installation
1. Install the package
Use the same package manager your Medusa project uses. In a monorepo, run it
from the backend app (e.g. apps/backend) — if you're already in that directory
you don't need a --workspace flag.
# pnpm (recommended if your project is pnpm-based)
pnpm add @techmarbles/medusa-odoo-connector
# npm
npm install @techmarbles/medusa-odoo-connectorPeer-dependency / React 19 note. If npm fails with
ERESOLVE(e.g. your storefront is on React 19, or your Medusa minor differs), install with:npm install @techmarbles/medusa-odoo-connector --legacy-peer-depsThis is safe for Medusa plugins — the admin UI runs on React 18 inside the backend, isolated from the storefront's React 19.
Using pnpm? Don't mix in
npm installafterwards — it corrupts pnpm's symlinkednode_modules(theCannot read properties of null (reading 'matches')error). Stick topnpm. If pnpm reports "Ignored build scripts", that's just pnpm 10's default; the build still works (runpnpm approve-buildsto clear it).
2. One-command setup
Run the bundled setup command from your backend directory. With --all it edits
medusa-config.ts, runs the DB migration, and builds the admin — everything but
the restart:
npx medusa-odoo-connector-setup --allThen just restart your server (pm2 / systemd / docker) and hard-refresh the
admin → Odoo appears in the sidebar. (Omit --all to only edit the config and
run db:migrate / build yourself — see step 3.)
Both entries are required. The plugins entry loads
the admin UI and API routes; the modules entry loads the settings store (without
it the admin menu appears but Setup won't save Odoo credentials):
module.exports = defineConfig({
// ...projectConfig
plugins: [
{ resolve: "@techmarbles/medusa-odoo-connector", options: {} },
],
modules: [
{ resolve: "@techmarbles/medusa-odoo-connector/modules/odoo" },
],
})3. Migrate, build, and restart (if you skipped --all)
npx medusa db:migrate # creates the connector's settings table
npx medusa build # bundles the Odoo admin pagesThen restart the running server process (pm2 / systemd / docker) — a build changes nothing until the process is restarted:
pm2 restart all # or your service manager / `npx medusa start`Hard-refresh the admin → Odoo appears in the sidebar.
4. Configure
No connector .env is required — the package defaults to the hosted odoo-api
(https://medusa-odoo-connector.devwork.in). Just open Admin → Odoo → Setup,
enter your Odoo credentials, and Save (this auto-registers your store on the
central odoo-api).
Optional env (Medusa backend .env):
# Enables background + scheduled syncs; without it the connector runs syncs
# inline. Usually already set, since Medusa itself needs Redis in production.
REDIS_URL=redis://localhost:6379
# Self-hosted odoo-api or a custom shared key
ODOO_API_URL=https://your-odoo-api.example.com
ODOO_API_KEY=your-keyTroubleshooting
| Symptom | Cause / fix |
|---------|-------------|
| No "Odoo" menu / 404 on /app/odoo | Plugin not in plugins[], or you didn't rebuild + restart after adding it. Verify grep techmarbles medusa-config.ts, then npx medusa build and restart the server process. |
| Setup page won't save Odoo credentials | The modules[] entry is missing from medusa-config.ts. Add it (step 2), then migrate, rebuild, restart. |
| ERESOLVE peer conflict (npm) | Install with --legacy-peer-deps, or use pnpm add. |
| Cannot read properties of null (reading 'matches') | You ran npm install in a pnpm project. Use pnpm add instead. |
| Plans page: "Stripe isn't configured" | Expected until you set STRIPE_SECRET_KEY on the odoo-api service (not the store) — see below. |
| Scheduled imports never run | REDIS_URL isn't set → the connector is in inline mode (no scheduler). Set REDIS_URL to enable background/scheduled syncs. |
| pnpm: "Ignored build scripts" | Harmless. Run pnpm approve-builds to clear it. |
The odoo-api companion service
The plugin proxies all real Odoo work to odoo-api (an Express + MySQL service) — it performs the Odoo JSON-RPC and optional Stripe billing, while Medusa stays the source of truth for configuration.
By default the plugin points at a hosted odoo-api, so most merchants deploy
nothing. To self-host, run your own odoo-api, set its MySQL connection, the
shared ODOO_API_KEY, and (optionally) STRIPE_* in its environment, then set
ODOO_API_URL / ODOO_API_KEY in the Medusa backend .env.
Redis & background processing
Redis is optional and, when used, is the same Redis Medusa already needs — the
connector reads the same REDIS_URL. There's nothing Redis-specific to configure
for the connector.
| Mode | When | Behaviour |
|------|------|-----------|
| Queued (recommended) | REDIS_URL is set | Syncs run in the background via BullMQ; scheduled imports available; large syncs don't block requests. |
| Inline (fallback) | REDIS_URL not set | Syncs run synchronously in the request. No Redis needed and it "just works" on a minimal store — but there's no scheduling, and big syncs block until done. |
Set REDIS_URL in the Medusa backend .env to enable queued mode (production stores
already have it, since Medusa's event bus / workflow engine need Redis too).
Stripe billing (optional)
Billing lives on odoo-api, not on the store. To sell tiered subscriptions:
- Set
STRIPE_SECRET_KEY(andSTRIPE_WEBHOOK_SECRET,BILLING_SYNC_SECRET) in the odoo-api environment, then restart it. - Manage plan prices in the operator dashboard at
/dashboard/pricing— each tier's price syncs to Stripe.
Without Stripe keys the connector still runs; everyone is treated as Free.
Development
npm run build # medusa plugin:build
npm run test:plugin # Vitest unit tests for the sync logic (run from the repo root)