const config = require('../config')
const ExtError = require('../libs/extended-error')
const _ = require('lodash')
const axios = require('axios')
const LibCash = require('@developers.cash/libcash-js')
// LibCash instance
const libCash = new LibCash()
const privateKey = libCash.ECPair.fromWIF(config.wif)
/**
* Webhooks
* @todo Refactor this into a service
*/
class Webhooks {
async start () {
console.log('[Webhooks] Starting')
}
async send (endpoint, event, payload) {
try {
payload = Object.assign(payload, { event: event })
const res = await axios.post(endpoint, payload, {
headers: this._buildHeader(payload)
})
// Throw an error if response code is not 200
if (res.status !== 200) {
throw ExtError(`Received ${res.status} response from endpoint`)
}
return res
} catch (err) {
console.log(err)
throw ExtError(`Webhook "${event}" failed with error "${err.message}".`, {
details: {
type: `Webhook ${event}`,
message: err.message
}
})
}
}
/**
* Use to send a webhook when an invoice is requested.
* @param invoice The Invoice
*/
async requested (invoice) {
await this.send(invoice.webhook, 'requested', { invoice: invoice.payload() })
}
/**
* Use to send a webhook when a transaction is broadcasting to the network.
* @param invoice The Invoice
*/
async broadcasting (invoice) {
const res = await this.send(invoice.webhook.broadcasting, 'broadcasting', { invoice: invoice.payload() })
// If JSON is returned, amend invoice
if (res.headers['content-type'] === 'application/json') {
Object.assign(
invoice,
_.pick(res.data, ['data', 'privateData'])
)
await invoice.save()
}
return res
}
/**
* Use to send a webhook when a transactoin is broadcasted to the network.
* @param invoice The Invoice
*/
async broadcasted (invoice) {
const res = await this.send(invoice.webhook.broadcasted, 'broadcasted', { invoice: invoice.payload() })
// If JSON is returned, amend invoice
if (res.headers['content-type'] === 'application/json') {
Object.assign(
invoice,
_.pick(res.data, ['data', 'privateData'])
)
await invoice.save()
}
return res
}
/**
* Use to send a webhook when a transactoin is broadcasted to the network.
* @param invoice The Invoice
*/
async confirmed (invoice) {
await this.send(invoice.webhook.confirmed, 'confirmed', { invoice: invoice.payload() })
}
/**
* Use to send a webhook when an error has occurred.
* @param req The ExpressJS Request
* @param err The Error thrown
* @param invoice The invoice
*/
static async error (req, err, invoice) {
const payload = {
type: 'error',
invoice: invoice.payload(true),
req: {
headers: req.headers,
query: req.query,
params: req.options,
body: req.body
},
error: {
message: err.message,
stack: err.stack
}
}
return await axios.post(invoice.options.webhooks.error, payload, {
headers: this._buildHeader(payload)
})
}
_buildHeader (payload) {
const digest = Buffer.from(libCash.Crypto.sha256(JSON.stringify(payload)), 'utf8')
const signature = libCash.ECPair.sign(privateKey, digest)
return {
digest: digest.toString('base64'),
'x-signature-type': 'ECC',
'x-identity': config.domain,
'x-signature': signature.toDER().toString('base64')
}
}
}
const webHooks = new Webhooks()
module.exports = webHooks