POS Gateway - Infrastructure

Files covered: apps.py, routing.py, urls.py, channels_middleware.py Last Updated: 2026-07-09

Overview

POS Gateway infrastructure covers Django app startup, HTTP URL routing, WebSocket URL routing, and WebSocket authentication middleware. The app has no signals.py; see Signals for why.


apps.py

PosGatewayConfig.ready() imports the services module and calls GatewayInitializationService.initialize() on startup, warming the product and store lookup caches. Any exception during initialization is caught and logged as a warning rather than raised, so a cache-warming failure never prevents the app from starting.


routing.py: WebSocket URL Routing

Two re_path patterns, both matched against [\w\-]+ (word characters and hyphens) for the captured ID:

  • ws/pos-gateway/terminal/<terminal_id>/TerminalConsumer
  • ws/pos-gateway/manager/<store_id>/ManagerConsumer

These patterns are mounted at the ASGI application root in api/nextango/asgi.py, wrapped in PosGatewaySessionMiddleware rather than the standard AuthMiddlewareStack (the gateway doesn't use the Django session cookie for WebSocket auth; see Authentication and Consumers).


urls.py: REST API URL Routing

Nine URL patterns under the pos_gateway namespace, mounted at /pos/ by the root api/nextango/urls.py (path('pos/', include('nextango.apps.pos_gateway.urls'))):

Path (relative to /pos/)View
auth/terminal-login/TerminalAuthenticationView
auth/terminal-logout/TerminalLogoutView
transactions/create/TransactionCreateView
transactions/<transaction_id>/payment/TransactionPaymentView
approvals/request/ManagerApprovalView
approvals/status/<request_id>/ManagerApprovalView
cash-drawer/CashDrawerView
health/HealthCheckView
audit/auth-event/AuditEventView

Full endpoint documentation, including request/response shapes and auth requirements per view, is in Views. Note the REST mount (/pos/...) and the WebSocket mount (/ws/pos-gateway/...) use different path prefixes; they are not related paths on the same resource tree.


channels_middleware.py: WebSocket Authentication

PosGatewaySessionMiddleware runs ahead of both consumers and authenticates the WebSocket handshake by reading a pos_session JWT from the ?token= query string (browsers cannot set custom headers on a WS upgrade). It populates scope['user'] and scope['pos_session'] and never rejects the handshake itself. A failed or missing token results in AnonymousUser and pos_session: None, leaving the accept/close decision to the consumer's own connect() method. See Authentication for the full validation flow and its DEBUG-only dev-token-* parity with the HTTP-side DevTokenAuthentication.


Channel Layer

The channel layer backend is channels_redis.pubsub.RedisPubSubChannelLayer, configured in nextango/settings/base.py and nextango/settings/production.py. This is a deliberate choice over channels_redis.core.RedisChannelLayer: the core layer's blocking read under redis-py 8 hits its socket read-timeout during idle periods and tears down WebSocket connections a few seconds after connect. The pubsub layer holds the subscription open without that idle timeout. capacity and expiry settings that apply to the core layer do not apply to the pubsub layer.


Deployment

WebSocket endpoints require an ASGI server (Daphne, Uvicorn, Hypercorn); HTTP endpoints can run on a traditional WSGI server. Production typically fronts both with a reverse proxy that routes /ws/ traffic to the ASGI process and everything else to the WSGI process.


Was this page helpful?