Skip to main content
8 min readEngineering

Building a Realtime Payment Dashboard with WebSockets

Architecting a sub-second transaction feed for merchants — handling token auth, connection lifecycle, mobile backgrounding, and debounced state sync across 155+ components.

SK

Sharath Kumar K R

Senior Software Engineer

Share
WebSocketVueArchitectureRealtime

When a customer taps their phone at a store in Japan, the merchant needs to see that transaction appear on their dashboard in under a second. Not 'eventually consistent'. Not 'refresh to see new results'. Right now, as it happens. I built that realtime feed — and the hardest part wasn't the WebSocket connection itself. It was everything around it.

The Real Challenge

The WebSocket implementation wasn't technically complex. What made it hard was the environment: mobile browsers with flaky networks, app backgrounding, auth token expiration, and 10+ components all needing the same stream of transaction data without 10 separate connections.

What we had to solve:

  • Token acquisition — scoped JWT per merchant session, refreshed before expiry
  • Singleton connection — one WebSocket shared across all dashboard components
  • Mobile lifecycle — close on background to save battery, reconnect with fresh token on foreground
  • Graceful degradation — auth failures (no retry), network transitions (wait and retry), server closures (immediate reconnect)
  • Debounced summary refresh — transaction list updates instantly, but revenue totals need to stabilize before refresh to avoid flickering

The Singleton Pattern

The core insight: 10 components each opening their own WebSocket would waste resources and create inconsistent states. A singleton client with a Vue mixin lifecycle provided a single connection with declarative consumption — connect, reconnect, auth refresh, cleanup — all in one place.

Custom close codes distinguished failure modes: 3001 for auth failure (never retry), 3002 for app backgrounding (stop gracefully), 3003 for network errors (retry with exponential backoff). This classification was critical for reliable operation on mobile devices.

Lessons Learned

  1. Realtime UX must account for mobile lifecycle, network recovery, auth refresh, and backpressure from day one. These are not edge cases — they're the normal operating environment.
  2. Debouncing state synchronization is more nuanced than it seems. Transaction lists can update instantly, but summary totals need to stabilize before refresh to prevent UI flickering.
  3. Production observability is non-negotiable for realtime systems. Every WebSocket event, reconnection, and error was instrumented through New Relic and Sentry.
The WebSocket approach eliminated 40+ unnecessary API calls per merchant per day and made the dashboard feel instant. But the real work was designing for the unreliable — mobile networks, app lifecycle, and auth expiration.