# ⚡ WebSocket Integration > **Real-time wallet balance updates** through LNbits WebSocket API with automatic reconnection, battery optimization, and smart unit conversion handling. ## Table of Contents - [[#Overview]] - [[#Architecture]] - [[#Connection Management]] - [[#Message Processing]] - [[#Balance Update Logic]] - [[#Error Handling]] - [[#Battery Optimization]] - [[#Configuration]] - [[#Troubleshooting]] ## Overview The Wallet WebSocket integration provides real-time synchronization between the Ario wallet interface and the LNbits Lightning backend. This enables instant balance updates and payment notifications without requiring page refreshes or manual polling. ### **Key Benefits** - **Instant UI Updates** - Balance changes appear immediately when payments are sent or received - **Payment Notifications** - Toast notifications for incoming payments - **Battery Efficient** - Pauses when app is not visible to save mobile battery - **Reliable Connection** - Automatic reconnection with exponential backoff - **Smart Conversion** - Handles LNbits sats/millisats behavior differences ### **WebSocket Endpoint** ``` wss://your-lnbits-server/api/v1/ws/{walletInkey} ``` Where `walletInkey` is the invoice/readonly key for the wallet. ## Architecture ### **Service Integration** ``` ┌─ WalletWebSocketService ─┐ ┌─ PaymentService ─┐ ┌─ UI Components ─┐ │ │ │ │ │ │ │ • WebSocket Connection │───▶│ • Balance Update │───▶│ • Balance Display│ │ • Message Processing │ │ • Wallet Mgmt │ │ • Toast Notifications │ • Reconnection Logic │ │ • Multi-wallet │ │ • Transaction List │ │ • Battery Optimization │ │ │ │ │ └──────────────────────────┘ └──────────────────┘ └──────────────────┘ │ │ │ │ ▼ ▼ ┌─ VisibilityService ─────┐ ┌─ AuthService ──────┐ │ │ │ │ │ • App Visibility │ │ • User Wallets │ │ • Connection Pausing │ │ • Authentication │ │ • Resume Management │ │ • Wallet Selection │ └─────────────────────────┘ └────────────────────┘ ``` ### **Dependency Graph** ``` WalletWebSocketService ├── BaseService (lifecycle management) ├── PaymentService (balance updates) ├── WalletService (transaction management) ├── AuthService (wallet credentials) └── VisibilityService (battery optimization) ``` ### **Initialization Flow** ``` 1. Module Installation ├── Create WalletWebSocketService instance ├── Register in DI container └── Initialize with dependencies 2. Service Initialization ├── Wait for dependencies (AuthService, PaymentService) ├── Register with VisibilityService ├── Set up auth event listeners └── Attempt initial connection 3. Authentication Events ├── auth:login → Connect WebSocket └── auth:logout → Disconnect WebSocket 4. Connection Lifecycle ├── Connect → Update status → Listen for messages ├── Message → Process → Update balance ├── Disconnect → Attempt reconnection └── Visibility Change → Pause/Resume ``` ## Connection Management ### **Connection Lifecycle** #### **Initial Connection** ```typescript // Triggered by authentication events eventBus.on('auth:login', () => { setTimeout(() => { this.connectIfNeeded() }, 500) // Small delay for auth processing }) ``` #### **Connection Process** ```typescript private async connect(walletInkey: string): Promise { // 1. Build WebSocket URL const baseUrl = import.meta.env.VITE_LNBITS_BASE_URL const wsProtocol = baseUrl.startsWith('https') ? 'wss:' : 'ws:' const host = baseUrl.replace(/^https?:\/\//, '').replace(/\/$/, '') const wsUrl = `${wsProtocol}//${host}/api/v1/ws/${walletInkey}` // 2. Create WebSocket connection this.ws = new WebSocket(wsUrl) // 3. Set up event handlers this.ws.onopen = this.handleOpen.bind(this) this.ws.onmessage = this.handleMessage.bind(this) this.ws.onclose = this.handleClose.bind(this) this.ws.onerror = this.handleWebSocketError.bind(this) } ``` ### **Reconnection Strategy** #### **Exponential Backoff** ```typescript private scheduleReconnect(): void { if (this.reconnectAttempts >= this.config.maxReconnectAttempts) { this.connectionStatus.value = 'failed' return } // Exponential backoff: 1s, 2s, 4s, 8s, 16s const delay = this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts) this.reconnectAttempts++ this.reconnectTimer = setTimeout(() => { this.connectIfNeeded() }, delay) } ``` #### **Connection Health Monitoring** ```typescript // Connection states tracked enum ConnectionStatus { DISCONNECTED = 'disconnected', CONNECTING = 'connecting', CONNECTED = 'connected', RECONNECTING = 'reconnecting', ERROR = 'error', FAILED = 'failed' } ``` ### **Graceful Disconnection** ```typescript public disconnect(): void { // Clear reconnection timer if (this.reconnectTimer) { clearTimeout(this.reconnectTimer) this.reconnectTimer = null } // Close WebSocket with normal closure code if (this.ws) { this.ws.close(1000, 'Client disconnect') this.ws = null } // Update reactive state this.isConnected.value = false this.connectionStatus.value = 'disconnected' } ``` ## Message Processing ### **Message Structure** LNbits WebSocket sends messages in this format: ```json { "wallet_balance": 1000820, // Balance in sats "payment": { // Payment details (optional) "amount": 100000, // Amount in millisats (positive = received, negative = sent) "payment_hash": "abc123...", "description": "Payment description", "time": 1694123456, "pending": false } } ``` ### **Message Handler** ```typescript private handleMessage(event: MessageEvent): void { try { const data = JSON.parse(event.data) // Process payment notification first if (data.payment) { this.handlePaymentNotification(data.payment) } // Process balance update if (data.wallet_balance !== undefined) { this.processBalanceUpdate(data) } } catch (error) { console.error('Failed to parse WebSocket message:', error) } } ``` ### **Payment Notification Processing** ```typescript private handlePaymentNotification(payment: any): void { // Add to transaction history via WalletService if (this.walletService?.addTransaction) { this.walletService.addTransaction(payment) } // Show toast notification for incoming payments if (payment.amount > 0 && !payment.pending) { const amountSats = Math.abs(payment.amount / 1000) this.toast.success(`Received ${amountSats} sats!`) } } ``` ## Balance Update Logic ### **LNbits WebSocket Behavior** LNbits has different behavior for incoming vs outgoing payments: - **Incoming payments**: WebSocket sends **post-payment balance** (already includes received amount) - **Outgoing payments**: WebSocket sends **pre-payment balance** (before deduction) ### **Smart Balance Conversion** ```typescript private processBalanceUpdate(data: any): void { let finalBalance = data.wallet_balance // Balance in sats from LNbits if (data.payment) { if (data.payment.amount < 0) { // Outgoing payment - LNbits sends pre-payment balance // We need to subtract the payment amount const paymentSats = Math.abs(data.payment.amount) / 1000 finalBalance = data.wallet_balance - paymentSats console.log('Outgoing payment adjustment:', { originalBalance: data.wallet_balance, paymentSats: paymentSats, finalBalance: finalBalance }) } else if (data.payment.amount > 0) { // Incoming payment - LNbits sends post-payment balance // Use balance as-is console.log('Incoming payment - using balance as-is:', data.wallet_balance) } } // Update balance via PaymentService this.updateWalletBalance(finalBalance) } ``` ### **Balance Update Integration** ```typescript private updateWalletBalance(balanceSats: number): void { // Convert sats to millisats for internal storage const balanceMsat = balanceSats * 1000 // Get connected wallet ID const wallet = this.paymentService?.getPreferredWallet?.() const walletId = wallet?.id // Update via PaymentService for consistency if (this.paymentService?.updateWalletBalance) { this.paymentService.updateWalletBalance(balanceMsat, walletId) } } ``` ### **Unit Conversion Table** | Source | Unit | Internal Storage | Display | |--------|------|------------------|---------| | LNbits WebSocket | sats | millisats × 1000 | sats ÷ 1000 | | LNbits API | millisats | millisats | sats ÷ 1000 | | Payment amounts | millisats | millisats | sats ÷ 1000 | | UI display | sats | millisats ÷ 1000 | sats | ## Error Handling ### **Connection Errors** ```typescript private handleWebSocketError(event: Event): void { console.error('WebSocket error:', event) this.connectionStatus.value = 'error' // Log additional error details if (this.ws) { console.error('WebSocket state:', this.ws.readyState) console.error('WebSocket URL:', this.ws.url) } } ``` ### **Connection Close Handling** ```typescript private handleClose(event: CloseEvent): void { console.log('WebSocket closed:', event.code, event.reason) this.isConnected.value = false this.connectionStatus.value = 'disconnected' this.ws = null // Only reconnect if not a normal closure if (event.code !== 1000) { this.scheduleReconnect() } } ``` ### **Error Recovery Strategies** #### **Authentication Errors (Code 1002)** ```typescript // Invalid wallet credentials - clear and require re-authentication if (event.code === 1002) { this.authService?.logout() this.toast.error('Wallet credentials invalid. Please login again.') } ``` #### **Network Errors** ```typescript // Network connectivity issues - retry with backoff if (!navigator.onLine) { window.addEventListener('online', () => { this.reconnectAttempts = 0 this.connectIfNeeded() }, { once: true }) } ``` #### **Server Errors (Code 1011)** ```typescript // Server internal error - longer backoff if (event.code === 1011) { this.config.reconnectDelay = 5000 // Increase delay this.scheduleReconnect() } ``` ## Battery Optimization ### **VisibilityService Integration** Mobile browsers suspend WebSocket connections when the app loses visibility. The WebSocket service integrates with VisibilityService to handle this efficiently: ```typescript protected async onInitialize(): Promise { // Register with VisibilityService if (this.visibilityService) { this.visibilityService.registerService( this.metadata.name, this.onResume.bind(this), this.onPause.bind(this) ) } } ``` ### **Pause Behavior** ```typescript private async onPause(): Promise { console.log('WebSocket pausing - app not visible') // Disconnect to save battery this.disconnect() } ``` ### **Resume Behavior** ```typescript private async onResume(): Promise { console.log('WebSocket resuming - app visible') // Reconnect if not already connected if (!this.isConnected.value) { this.reconnectAttempts = 0 // Reset attempt counter await this.connectIfNeeded() } } ``` ### **Visibility States** | App State | WebSocket Action | Battery Impact | |-----------|------------------|----------------| | Visible | Connected | Normal | | Hidden/Background | Disconnected | Minimal | | Tab Switch | Disconnected | Minimal | | Resume | Reconnect | Brief spike | ## Configuration ### **WebSocket Configuration** Configure WebSocket behavior in module config: ```typescript // app.config.ts modules: { wallet: { enabled: true, config: { websocket: { enabled: true, // Enable WebSocket functionality reconnectDelay: 1000, // Initial reconnection delay (ms) maxReconnectAttempts: 5 // Maximum reconnection attempts } } } } ``` ### **Environment Variables** ```bash # LNbits server URL - must be accessible for WebSocket VITE_LNBITS_BASE_URL=https://your-lnbits-server.com # Development mode - enables verbose WebSocket logging VITE_DEV_MODE=true ``` ### **Runtime Configuration Access** ```typescript // Access configuration in service const appConfig = (window as any).appConfig if (appConfig?.modules?.wallet?.config?.websocket) { this.config = { ...this.config, ...appConfig.modules.wallet.config.websocket } } ``` ## Troubleshooting ### **Common Issues** #### **WebSocket Connection Fails** **Symptoms:** Connection status shows 'error' or 'failed' **Debugging Steps:** 1. Check LNbits server accessibility 2. Verify VITE_LNBITS_BASE_URL environment variable 3. Check browser network tab for WebSocket connection attempts 4. Verify wallet credentials (inkey) are valid **Solutions:** ```bash # Test LNbits server connectivity curl https://your-lnbits-server.com/api/v1/wallet # Check WebSocket endpoint manually # Open browser console and test: const ws = new WebSocket('wss://your-server/api/v1/ws/your-inkey') ``` #### **Balance Not Updating** **Symptoms:** Payments are processed but UI balance doesn't change **Debugging Steps:** 1. Check browser console for WebSocket messages 2. Verify PaymentService is receiving updateWalletBalance calls 3. Check if wallet ID matches between services **Debug Logging:** ```typescript // Enable debug logging in WalletWebSocketService console.log('WebSocket message:', data) console.log('Balance update:', { old: oldBalance, new: finalBalance }) ``` #### **Frequent Disconnections** **Symptoms:** WebSocket constantly reconnecting **Potential Causes:** - Network instability - LNbits server restarts - Proxy/firewall interference - Mobile browser background throttling **Solutions:** - Increase reconnectDelay in configuration - Check server logs for connection issues - Verify VisibilityService is working correctly ### **Diagnostic Tools** #### **Connection Status Component** ```vue ``` #### **Debug Console Commands** ```javascript // Access services from browser console const wsService = window.__DI_CONTAINER__.get('WALLET_WEBSOCKET_SERVICE') // Check connection status console.log('Connected:', wsService.isConnected.value) console.log('Status:', wsService.connectionStatus.value) // Manual reconnection wsService.reconnect() // View configuration console.log('Config:', wsService.config) ``` ### **Performance Monitoring** #### **Connection Metrics** ```typescript // Track connection performance class ConnectionMetrics { private connectionAttempts = 0 private successfulConnections = 0 private totalDowntime = 0 private lastConnectTime = 0 onConnectionAttempt() { this.connectionAttempts++ this.lastConnectTime = Date.now() } onConnectionSuccess() { this.successfulConnections++ const connectTime = Date.now() - this.lastConnectTime console.log(`Connected in ${connectTime}ms`) } getSuccessRate(): number { return this.successfulConnections / this.connectionAttempts } } ``` ## See Also ### Related Documentation - **[[index|💰 Wallet Module Overview]]** - Main wallet module documentation - **[[../../03-core-services/visibility-service|👁️ Visibility Service]]** - Battery optimization service - **[[../../03-core-services/payment-service|💳 Payment Service]]** - Core payment processing ### API References - **[[../../05-api-reference/lnbits-api|⚡ LNbits API]]** - Backend API documentation - **[[../../05-api-reference/websocket-protocol|🔌 WebSocket Protocol]]** - WebSocket message formats ### Development Guides - **[[../../04-development/testing|🧪 Testing Guide]]** - Testing WebSocket functionality - **[[../../04-development/debugging|🐛 Debugging]]** - Debugging WebSocket issues --- **Tags:** #websocket #real-time #lightning #lnbits #battery-optimization #connection-management **Last Updated:** 2025-09-18 **Author:** Development Team