Skip to main content

Overview

BOOP Network uses WebSocket connections for real-time, bidirectional communication between your vendor system and our authentication service. This enables instant palm vein authentication without polling.

Connection Setup

Development Environment

Connect to the BOOP test network WebSocket endpoint:
const ws = new WebSocket('wss://dev.app.boop.it/ws/vendor');

Authentication

After connecting, authenticate with your vendor credentials:
ws.onopen = () => {
  ws.send(JSON.stringify({
    type: 'vendor_hello',
    api_key: 'your_api_key_here',
    vendor_id: 'your_vendor_id'
  }));
};

Complete Integration Example

const WebSocket = require('ws');

class BOOPVendorClient {
  constructor(apiKey, vendorId) {
    this.apiKey = apiKey;
    this.vendorId = vendorId;
    this.ws = null;
    this.activeContexts = new Map();
  }

  connect() {
    this.ws = new WebSocket('wss://dev.app.boop.it/ws/vendor');

    this.ws.on('open', () => {
      console.log('Connected to BOOP Network');
      this.authenticate();
    });

    this.ws.on('message', (data) => {
      const message = JSON.parse(data);
      this.handleMessage(message);
    });

    this.ws.on('error', (error) => {
      console.error('WebSocket error:', error);
    });

    this.ws.on('close', () => {
      console.log('Disconnected from BOOP Network');
      // Implement reconnection logic here
      setTimeout(() => this.connect(), 5000);
    });
  }

  authenticate() {
    this.send({
      type: 'vendor_hello',
      api_key: this.apiKey,
      vendor_id: this.vendorId
    });
  }

  createAuthContext(contextType, attributes, metadata = {}) {
    const contextId = this.generateContextId();

    const context = {
      type: 'create_context',
      context_id: contextId,
      context_type: contextType,
      attributes: attributes,
      metadata: metadata
    };

    this.activeContexts.set(contextId, {
      created: new Date(),
      status: 'pending'
    });

    this.send(context);
    return contextId;
  }

  handleMessage(message) {
    switch (message.type) {
      case 'vendor_hello_response':
        if (message.success) {
          console.log('Authentication successful');
        } else {
          console.error('Authentication failed:', message.error);
        }
        break;

      case 'context_created':
        console.log(`Context ${message.context_id} created successfully`);
        this.updateContextStatus(message.context_id, 'active');
        break;

      case 'authentication_success':
        console.log('User authenticated successfully');
        this.handleAuthSuccess(message);
        break;

      case 'authentication_failed':
        console.log('Authentication failed:', message.reason);
        this.handleAuthFailure(message);
        break;

      case 'error':
        console.error('Error from server:', message.message);
        break;

      default:
        console.log('Unknown message type:', message.type);
    }
  }

  handleAuthSuccess(message) {
    const { context_id, user_id, attributes } = message;

    // Process successful authentication
    console.log(`User ${user_id} authenticated for context ${context_id}`);
    console.log('User attributes:', attributes);

    // Update your system with the authentication result
    this.updateContextStatus(context_id, 'completed');

    // Perform business logic (e.g., complete payment, grant access)
    this.processSuccessfulAuth(user_id, attributes);
  }

  handleAuthFailure(message) {
    const { context_id, reason } = message;

    // Handle failed authentication
    console.log(`Authentication failed for context ${context_id}: ${reason}`);
    this.updateContextStatus(context_id, 'failed');
  }

  processSuccessfulAuth(userId, attributes) {
    // Implement your business logic here
    // For example: complete a payment, open a door, etc.
  }

  updateContextStatus(contextId, status) {
    const context = this.activeContexts.get(contextId);
    if (context) {
      context.status = status;
      context.updated = new Date();
    }
  }

  send(data) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    } else {
      console.error('WebSocket is not connected');
    }
  }

  generateContextId() {
    return `ctx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  disconnect() {
    if (this.ws) {
      this.ws.close();
    }
  }
}

// Usage example
const client = new BOOPVendorClient('your_api_key', 'your_vendor_id');
client.connect();

// Create an authentication context for a payment
setTimeout(() => {
  const contextId = client.createAuthContext(
    'payment',
    ['email', 'name'],
    {
      amount: 50.00,
      currency: 'USD',
      description: 'Coffee purchase'
    }
  );
  console.log(`Created authentication context: ${contextId}`);
}, 2000);

Message Types

Vendor → BOOP

Message TypeDescriptionRequired Fields
vendor_helloInitial authenticationapi_key, vendor_id
create_contextCreate authentication contextcontext_id, context_type, attributes
cancel_contextCancel active contextcontext_id
pingKeep connection alive-

BOOP → Vendor

Message TypeDescriptionFields
vendor_hello_responseAuthentication resultsuccess, error
context_createdContext creation confirmedcontext_id, qr_code
authentication_successUser authenticatedcontext_id, user_id, attributes
authentication_failedAuthentication failedcontext_id, reason
context_expiredContext timed outcontext_id
pongKeep-alive response-

Context Types

Payment Context

For processing payments:
client.createAuthContext(
  'payment',
  ['email', 'name', 'phone'],
  {
    amount: 100.00,
    currency: 'USD',
    merchant_reference: 'ORDER-12345',
    items: [
      { name: 'Widget', price: 100.00, quantity: 1 }
    ]
  }
);

Access Context

For physical or digital access control:
client.createAuthContext(
  'access',
  ['employee_id', 'department'],
  {
    location: 'Building A - Floor 3',
    access_level: 'restricted',
    duration: 3600 // seconds
  }
);

Age Verification Context

For age-restricted purchases:
client.createAuthContext(
  'age_verification',
  ['age_verified'],
  {
    minimum_age: 21,
    product_type: 'alcohol',
    transaction_id: 'TXN-98765'
  }
);

Error Handling

Connection Errors

Implement automatic reconnection with exponential backoff:
class ReconnectingClient extends BOOPVendorClient {
  constructor(apiKey, vendorId) {
    super(apiKey, vendorId);
    this.reconnectAttempts = 0;
    this.maxReconnectDelay = 30000; // 30 seconds
  }

  async handleDisconnect() {
    const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), this.maxReconnectDelay);
    console.log(`Reconnecting in ${delay}ms...`);

    await new Promise(resolve => setTimeout(resolve, delay));
    this.reconnectAttempts++;

    try {
      await this.connect();
      this.reconnectAttempts = 0; // Reset on successful connection
    } catch (error) {
      console.error('Reconnection failed:', error);
      await this.handleDisconnect(); // Retry
    }
  }
}

Message Validation

Always validate incoming messages:
validateMessage(message) {
  // Check required fields based on message type
  const requiredFields = {
    'authentication_success': ['context_id', 'user_id'],
    'authentication_failed': ['context_id', 'reason'],
    'context_created': ['context_id']
  };

  const required = requiredFields[message.type];
  if (required) {
    for (const field of required) {
      if (!(field in message)) {
        throw new Error(`Missing required field: ${field}`);
      }
    }
  }

  return true;
}

Best Practices

  • Never expose your API key in client-side code
  • Use environment variables for credentials
  • Implement request signing for additional security
  • Validate all incoming messages
  • Use TLS/SSL for all connections
  • Implement connection pooling for high-volume scenarios
  • Use message queuing for reliability
  • Batch context creation when possible
  • Monitor WebSocket health with regular pings
  • Implement circuit breakers for failing connections
  • Always implement reconnection logic
  • Store context state locally for recovery
  • Use unique, idempotent context IDs
  • Implement timeout handling for contexts
  • Log all messages for debugging
  • Use the dev environment for all testing
  • Test edge cases like network disconnections
  • Verify attribute handling
  • Test with various context types
  • Monitor message latency

Testing Your Integration

1. Basic Connection Test

// Test connection and authentication
const testConnection = async () => {
  const client = new BOOPVendorClient('test_api_key', 'test_vendor_id');

  client.ws.on('open', () => {
    console.log('✓ Connected successfully');
  });

  client.ws.on('message', (data) => {
    const message = JSON.parse(data);
    if (message.type === 'vendor_hello_response' && message.success) {
      console.log('✓ Authentication successful');
    }
  });

  client.connect();
};

2. Context Creation Test

// Test creating and handling contexts
const testContextCreation = async () => {
  const client = new BOOPVendorClient('test_api_key', 'test_vendor_id');
  await client.connect();

  // Create test context
  const contextId = await client.createAuthContext(
    'payment',
    ['email'],
    { amount: 1.00, test: true }
  );

  console.log(`✓ Context created: ${contextId}`);
};

3. End-to-End Test

Use our mock PVS interface to simulate complete authentication flows:
  1. Create a context via WebSocket
  2. Display the QR code or context ID
  3. Use the mock PVS at https://mock-pvs.dev.event-vendor.boop.it to simulate a scan
  4. Receive authentication result via WebSocket

Migration to Production

When ready to go live:
  1. Change WebSocket URL:
    // Development
    const ws = new WebSocket('wss://dev.app.boop.it/ws/vendor');
    
    // Production
    const ws = new WebSocket('wss://app.boop.it/ws/vendor');
    
  2. Update API credentials to production keys
  3. Remove test flags from metadata
  4. Implement production monitoring and alerting

Next Steps