POS bridge & ECR Hub
What you’ll find here
Section titled “What you’ll find here”- What the POS bridge is and why every lane needs one.
- How the ECR Hub in Vendor Desktop reaches the hardware.
- How to read the heartbeat and activity log when something stops working.
I. What the POS bridge is
Section titled “I. What the POS bridge is”The POS bridge is a small local service that runs on the lane PC (or the Vendor Desktop app). It is the only thing in the system that talks to:
- The receipt printer (ESC/POS or StarPRNT).
- The card terminal over LAN (PAX) or WebSocket (CodePay).
- The cash drawer (pulses the open signal).
- The scale (USB / serial).
- The customer display (secondary screen).
The browser / dashboard never talks to these devices directly. Instead:
Browser (vendor-web) ⇄ NestJS API ⇄ POS bridge (Vendor Desktop / lane PC) ⇄ HardwareThe authoritative relay is the bridge — even though apps/vendor-web also has a “probe” WebSocket for the same URL (used for heartbeats and settings), the real work happens on the desktop.
II. ECR Hub (inside Vendor Desktop)
Section titled “II. ECR Hub (inside Vendor Desktop)”ECR Hub is the module inside Vendor Desktop that:
- Opens a WebSocket to the bridge URL stored on the terminal store device.
- Translates high-level events (
sale.placed,payment.intent) into vendor-protocol calls to PAX or CodePay. - Streams back terminal status (idle / card-read / payment-summary / signature-needed).
The bridge URL is built by the backend:
POST /api/v1/vendor/settings/devices/pos-bridge/activatewith the active store_id and terminal id — the response contains the WebSocket URL the lane PC opens.
III. Pair the bridge
Section titled “III. Pair the bridge”Settings → Devices → Terminals (POS bridge).
You’ll see one entry per store device. Pick the active terminal and tap Activate. The dashboard shows:
- Bridge URL (read-only — copy and paste into Vendor Desktop).
- Status —
connected/reconnecting/offline. - Last heartbeat — most recent ping from Vendor Desktop.
- Activity log — every sale + payment event.
If the bridge is offline, every hardware interaction on that lane falls back to “manual mode” — receipts queue, card capture requires an owner click.
IV. Reading the activity log
Section titled “IV. Reading the activity log”| Event | What it means |
|---|---|
bridge.connect | Vendor Desktop opened the WebSocket. |
bridge.heartbeat | A ping from the desktop (every ~30s). |
print.success / print.error | A receipt finished printing (or failed). |
terminal.intent | A payment intent was sent to the card terminal. |
terminal.capture | The terminal confirmed capture. |
terminal.void | The terminal confirmed a post-capture void. |
bridge.disconnect | The desktop closed the socket. |
Use this log to debug “the cashier said the printer didn’t print” — the log tells you whether the desktop actually sent the job.
V. Distinguish transport on the device row
Section titled “V. Distinguish transport on the device row”The device row carries two distinct sets of fields:
terminalWsUrl— forcodepay_ws(a CodePay gateway WebSocket URL).pax_lan— LAN IP / port forpax_lanPAX devices.
The UI lists the right fields for the device’s terminalRelayKind. Don’t mix them — copying a CodePay URL onto a PAX device produces a “press 0 on terminal” error from the ECR Hub.
VI. Heartbeat colors
Section titled “VI. Heartbeat colors”- Green — desktop is connected and pinging.
- Yellow — desktop lost contact within the last minute; the dashboard will retry.
- Red — desktop offline for over 2 minutes. Receipts queue; card payments need manual capture.
VII. Common pitfalls
Section titled “VII. Common pitfalls”Bridge is green but the printer doesn’t print. The bridge is reachable, but the printer behind it isn’t. See Receipt printer troubleshooting.
Bridge red after a Vendor Desktop update. Restart the desktop. The WebSocket URL is regenerated on each activate, so a stale URL is the usual cause.
Two lanes share one bridge. Not recommended — the bridge is one-to-one with each terminalPos. Pair a second bridge for the second lane.
Card payment stuck on “pending”. Read Card terminal → “Pending stays for minutes”.