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>/→TerminalConsumerws/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.
Related Documentation
- Services:
GatewayInitializationServicecache warming - Consumers:
TerminalConsumerandManagerConsumer - Views: REST endpoint implementations
- Authentication: JWT session and WebSocket middleware auth
- Broadcast Utilities: server-initiated WebSocket broadcasts