'use strict'
const config = require('../config')
const SocketIO = require('socket.io')
const LibCash = require('@developers.cash/libcash-js')
// LibCash instance
const libCash = new LibCash()
const privateKey = libCash.ECPair.fromWIF(config.wif)
/**
* WebSocket Library for Notifying Clients of Payment Events
*/
class WebSocket {
constructor () {
this.subscriptions = {}
}
/**
* Setup the Websocket server
*/
async startServer (server) {
// Setup Websockets
const socket = SocketIO(server, {
pingTimeout: 5000,
pingInterval: 25000
})
socket.on('connection', (client) => this._onConnection(client))
}
/**
* Notify any Websocket connections that are subscribed to
* the given invoiceId that an event has occurred.
* @param invoiceId The ID of the invoice
* @param event The event that was triggered
* @param invoice The Invoice data
*/
async notify (invoiceId, event, invoice) {
try {
if (this.subscriptions[invoiceId]) {
// Compile payload
var payload = Object.assign({ event: event }, invoice)
// Append signature
payload = Object.assign(payload, { signature: this._buildSignature(payload) })
for (const client of this.subscriptions[invoiceId]) {
client.emit(event, payload)
}
}
} catch (err) {
console.log(err)
}
}
/**
* Triggered when a Websocket client connects
* @param ws The Websocket of the client
* @private
*/
async _onConnection (client) {
console.log(`[Websockets] Connected ${client.id}`)
// Setup event listeners
client.on('subscribe', (msg) => this._onSubscribe(client, msg))
client.on('unsubscribe', (msg) => this._onUnsubscribe(client, msg))
client.on('disconnect', (msg) => this._onDisconnect(client, msg))
}
/**
* Triggered when a Websocket client subscribes to an Invoice ID
* @param client The Websocket of the client
* @param msg The payload
* @private
*/
async _onSubscribe (client, msg) {
// TODO Make sure invoice exists
// We really want to do this as soon as client connects.
// If we don't, then we open ourselves up to DoS attacks.
// So. perhaps, we force a subscribe within X seconds.
// Otherwise, we boot the client for being cunts.
if (!this.subscriptions[msg.invoiceId]) {
this.subscriptions[msg.invoiceId] = []
}
this.subscriptions[msg.invoiceId].push(client)
return client.emit('subscribed', {
message: `Subscribed to ${msg.invoiceId}`
})
}
/**
* Triggered when a Websocket client unsubscribes from an Invoice ID
* @param client The Websocket of the client
* @param msg The payload
* @private
*/
async _onUnsubscribe (client, msg) {
delete this.subscriptions[msg.invoiceId]
return client.emit('unsubscribed', {
message: `Unsubscribed from ${msg.invoiceId}`
})
}
async _onDisconnect (client, msg) {
console.log(`[Websockets] Disconnected ${client.id}: ${msg}`)
delete this.subscriptions[msg.invoiceId]
}
_buildSignature (payload) {
const digest = Buffer.from(libCash.Crypto.sha256(JSON.stringify(payload)), 'utf8')
const signature = libCash.ECPair.sign(privateKey, digest)
return {
digest: digest.toString('base64'),
signatureType: 'ECC',
identity: config.domain,
signature: signature.toDER().toString('base64')
}
}
}
const webSocket = new WebSocket()
module.exports = webSocket