{"_id":"@accurateitsolutionorg/mergeport-integration","name":"@accurateitsolutionorg/mergeport-integration","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@accurateitsolutionorg/mergeport-integration","version":"0.1.0","description":"Transport layer connecting a POS to Mergeport (ordering.mergeport.com/v4): order sync, order state, POS item/category sync, and per-restaurant order webhooks. No persistence — your backend owns storage.","main":"src/index.js","keywords":["mergeport","pos","ordering","integration"],"license":"UNLICENSED","publishConfig":{"access":"public"},"engines":{"node":">=18"},"scripts":{"example":"node examples/expressApp.js"},"dependencies":{"axios":"^1.7.9","express":"^4.21.2"},"devDependencies":{"dotenv":"^16.4.5","mysql2":"^3.11.5"},"_id":"@accurateitsolutionorg/mergeport-integration@0.1.0","_nodeVersion":"23.11.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-X1ACPIQzRkH8VYCgvKHr/Ek1myijEEwPd+V5aLgiWe2E2LB9EtPWPFN88BaaajZcDbgz3AWO6INFvfdRwut+CA==","shasum":"950df01b80848ffd69b1afe5128deb9b19947ae4","tarball":"https://registry.npmjs.org/@accurateitsolutionorg/mergeport-integration/-/mergeport-integration-0.1.0.tgz","fileCount":6,"unpackedSize":16985,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD33MaNTXsghI8htKV++KZ6hN+8hj6gauUT6roUsxUk1wIgZf8rp7J80lsZZPWffgXO9dHassHp9RIzYURsrjlqm04="}]},"_npmUser":{"name":"accurateitsolutionorg","email":"vatharsh@gmail.com"},"directories":{},"maintainers":[{"name":"accurateitsolutionorg","email":"vatharsh@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mergeport-integration_0.1.0_1785148976923_0.5660884385751335"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-27T10:42:56.814Z","0.1.0":"2026-07-27T10:42:57.057Z","modified":"2026-07-27T10:42:57.216Z"},"maintainers":[{"name":"accurateitsolutionorg","email":"vatharsh@gmail.com"}],"description":"Transport layer connecting a POS to Mergeport (ordering.mergeport.com/v4): order sync, order state, POS item/category sync, and per-restaurant order webhooks. No persistence — your backend owns storage.","keywords":["mergeport","pos","ordering","integration"],"license":"UNLICENSED","readme":"# mergeport-integration\n\nA transport layer connecting a POS to Mergeport (`ordering.mergeport.com/v4`) —\nnothing more. It talks to the four core API calls, receives order webhooks, and routes\nboth by restaurant for multi-tenant setups. **It does not persist anything.** Orders,\norder-state history, item mapping, and even where restaurant credentials live long-term\nare entirely your backend's responsibility.\n\n## Core concepts\n\n- **`MergeportClient`** — bound to one restaurant's Mergeport `apiKey`. Exposes\n  `getOrders`, `getOrder`, `setOrderState`, `setPosItems`, `setCategoryItems`.\n- **`MergeportRegistry`** — in-memory map of `restaurantId -> MergeportClient`, so one\n  process can talk to many restaurants' Mergeport accounts. `register()` is the\n  \"registration process\" — pass the apiKey (and optionally a webhookSecret) for a\n  restaurant and it's ready to use. Purely in-memory: your backend calls `register()`\n  for each restaurant at startup (loading credentials from wherever *it* stores them)\n  and again whenever a restaurant is onboarded or its key is rotated.\n- **Webhook router** — `createMergeportWebhookRouter({ registry, onOrderEvent })` is an\n  Express router with a per-restaurant route (`/:restaurantId/orders`), so each\n  restaurant gets its own webhook URL to register in the Mergeport dashboard. Your\n  `onOrderEvent` handler receives `{ restaurantId, order, client }` and decides what to\n  do with the order — this package doesn't write it anywhere.\n\n## Layout\n\n- `src/MergeportClient.js` — the API client class.\n- `src/registry/MergeportRegistry.js` — in-memory registry.\n- `src/webhooks/router.js` — per-restaurant webhook router.\n- `src/index.js` — package entry point (`MergeportClient`, `MergeportRegistry`,\n  `createMergeportWebhookRouter`).\n- `examples/expressApp.js` — a stand-in for *your* backend: owns its own MariaDB\n  tables, loads restaurant credentials from them, registers each with the package, and\n  persists incoming orders itself.\n- `examples/host-schema.example.sql` — illustrative tables for the example only, not\n  shipped by or required by this package.\n\n## Setup\n\n```bash\nnpm install\n```\n\nTo run the example (which simulates a host backend with its own DB):\n\n```bash\nmysql -u root -p pos < examples/host-schema.example.sql\ncp .env.example .env\nnpm run example\n```\n\n## Using it in your backend\n\n```js\nconst { MergeportRegistry, createMergeportWebhookRouter } = require('mergeport-integration');\n\nconst registry = new MergeportRegistry();\n\n// Load however your backend stores restaurant <-> Mergeport credentials\n// (your own MariaDB table, a secrets manager, env vars — up to you):\nfor (const restaurant of await yourBackend.loadRestaurants()) {\n  registry.register(restaurant.id, {\n    apiKey: restaurant.mergeportApiKey,\n    webhookSecret: restaurant.mergeportWebhookSecret,\n  });\n}\n\n// Onboarding a new restaurant later:\nregistry.register(newRestaurantId, { apiKey, webhookSecret });\n\n// Mount the webhook router — each restaurant's webhook URL becomes\n// https://<host>/webhooks/mergeport/<restaurantId>/orders\napp.use('/webhooks/mergeport', createMergeportWebhookRouter({\n  registry,\n  async onOrderEvent({ restaurantId, order, client }) {\n    await yourBackend.saveOrder(restaurantId, order);\n    // await client.setOrderState(order.id, { state: 'acceptedByPOS' });\n  },\n}));\n\n// Use a restaurant's client directly elsewhere:\nconst client = registry.get(restaurantId);\nawait client.getOrders({ filter: 'active' });\n```\n\n## Verified against Mergeport's actual OpenAPI spec\n\n`ordering.mergeport.com/v4/documentation` is a ReDoc page with the full OpenAPI 3.0.3\nspec embedded inline (`__redoc_state` in the page's HTML). This isn't a live account,\nbut it's the real spec, not a scrape/summary — pulling it directly caught two real bugs\nin the first draft of this client:\n\n- **`SetOrderState` is `PATCH /pos/orders/{id}`**, not `PUT` as the vendor email said.\n  Fixed in `src/MergeportClient.js`.\n- **`SetPosItems` / `SetCategoryItems` request bodies are bare arrays**\n  (`[{...}, {...}]`), not `{ items: [...] }` / `{ categories: [...] }` as first\n  implemented. Fixed.\n\nAlso confirmed directly from the spec: `Authorization` header carries the raw apiKey\n(no `Bearer` prefix) for every call; the full `state` enum is `receivedByProvider`,\n`fetchedByPOS`, `canceledByProvider`, `canceledByPOS`, `rejectedByPOS`, `acceptedByPOS`,\n`preparing`, `ready`, `pickedUp`, `inDelivery`, `delivered`; `GetOrders` (200) returns\n`{ orders: [...], startKey }`; `SetOrderState` (200) returns `{ success, order }`;\n`PosItemRest` requires at least an `id`. `setOrderState` also now accepts an optional\n`receipts` array — the spec has this on the same endpoint for sending invoice/tax data\nback for the provider to print a receipt (a separate concern from Mergeport not sending\ntax data *to* you, which still stands).\n\nAlso present in the spec but not built here (only add if you need it): dedicated\n`GET /pos/orders/active` / `/pos/orders/all`, a `fetchedByPOS` state for marking an\norder \"downloaded\" without accepting it yet, `SetPosMenus`/menu endpoints, and a full\nWebSocket alternative (`wss://socket.ordering.mergeport.com/v4`, auth via\n`Sec-WebSocket-Protocol` or `?accesstoken=`) for POS systems that can't expose a public\nwebhook URL.\n\n## Still unverified / genuinely undocumented\n\n- **Webhook signature/secret scheme**: the spec's prose says only \"configure webhook\n  URL + secret in your integration\" for the *order* webhook — it does not document which\n  header carries that secret or whether it's HMAC-signed. (There's a `webhookSigningMode`\n  field with `legacy`/`standard-webhooks` values elsewhere in the spec, but that's under\n  the provider-channel config schema — Mergeport's own connection to delivery platforms\n  — not confirmed to apply to the order webhook sent to you.) `src/webhooks/router.js`\n  checks a plain shared-secret header (`x-mergeport-secret`) as a placeholder; get the\n  real scheme from your dashboard or support once you have an account.\n- **One webhook URL per restaurant vs. one for your whole integration**: assumed\n  per-restaurant here (hence the `:restaurantId` path segment). If Mergeport only\n  supports a single webhook URL account-wide, derive `restaurantId` from a field in the\n  payload (e.g. `siteId`) instead.\n- **Where you store credentials**: this package holds apiKeys only in memory\n  (`MergeportRegistry`). Wherever your backend persists them long-term, treat them as\n  secrets (encryption at rest / secrets manager).\n","readmeFilename":"README.md","_rev":"1-ee0f61e9eb99743c9e339270e9b34e21"}