# ⚡ 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<void> {
  // 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<void> {
  // 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<void> {
  console.log('WebSocket pausing - app not visible')

  // Disconnect to save battery
  this.disconnect()
}
```

### **Resume Behavior**
```typescript
private async onResume(): Promise<void> {
  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
<template>
  <div class="websocket-status">
    <div :class="statusClass">
      {{ connectionStatus }}
    </div>
    <div v-if="!isConnected">
      <button @click="reconnect">Reconnect</button>
    </div>
  </div>
</template>

<script setup>
const websocketService = injectService(SERVICE_TOKENS.WALLET_WEBSOCKET_SERVICE)
const { isConnected, connectionStatus } = websocketService

const statusClass = computed(() => ({
  'status-connected': connectionStatus.value === 'connected',
  'status-connecting': connectionStatus.value === 'connecting',
  'status-error': connectionStatus.value === 'error'
}))

const reconnect = () => websocketService.reconnect()
</script>
```

#### **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