invoice cb
This commit is contained in:
parent
c877c806b4
commit
239a5603f4
2 changed files with 153 additions and 69 deletions
|
|
@ -14,7 +14,6 @@ const {
|
||||||
} = Common
|
} = Common
|
||||||
const SchemaManager = require('../../../schema')
|
const SchemaManager = require('../../../schema')
|
||||||
const LightningServices = require('../../../../utils/lightningServices')
|
const LightningServices = require('../../../../utils/lightningServices')
|
||||||
const LNDHealthManager = require('../../../../utils/lightningServices/errors')
|
|
||||||
|
|
||||||
const Key = require('../key')
|
const Key = require('../key')
|
||||||
const Utils = require('../utils')
|
const Utils = require('../utils')
|
||||||
|
|
@ -105,7 +104,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const listenerStartTime = performance.now()
|
//const listenerStartTime = performance.now()
|
||||||
|
|
||||||
ordersProcessed.add(orderID)
|
ordersProcessed.add(orderID)
|
||||||
|
|
||||||
|
|
@ -228,74 +227,113 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
||||||
|
|
||||||
const invoicePutEndTime = performance.now() - invoicePutStartTime
|
const invoicePutEndTime = performance.now() - invoicePutStartTime
|
||||||
|
|
||||||
// invoices should be settled right away so we can rely on this single
|
|
||||||
// subscription instead of life-long all invoices subscription
|
|
||||||
/**
|
|
||||||
* @type {string|null}
|
|
||||||
*/
|
|
||||||
let maybePostId = null
|
|
||||||
if (order.targetType === 'post') {
|
|
||||||
const { postID } = order
|
|
||||||
maybePostId = postID || null
|
|
||||||
if (!Common.isPopulatedString(postID)) {
|
|
||||||
throw new TypeError(`postID not a a populated string`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { r_hash } = invoice
|
|
||||||
|
|
||||||
// A post tip order lifecycle is short enough that we can do it like this.
|
|
||||||
const stream = LightningServices.invoices.subscribeSingleInvoice({
|
|
||||||
r_hash
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Common.Invoice & {r_hash:Buffer}} invoice
|
|
||||||
*/
|
|
||||||
const invoiceSubCb = invoice => {
|
|
||||||
if (!invoice.settled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (order.targetType === 'post' && typeof maybePostId === 'string') {
|
|
||||||
getUser()
|
|
||||||
.get('postToTipCount')
|
|
||||||
.get(maybePostId)
|
|
||||||
.set(null) // each item in the set is a tip
|
|
||||||
}
|
|
||||||
const myLndPub = LNDHealthManager.lndPub
|
|
||||||
const myGunPub = getUser()._.sea.pub
|
|
||||||
if (!myLndPub) {
|
|
||||||
return //should never happen but just to be safe
|
|
||||||
}
|
|
||||||
SchemaManager.AddOrder({
|
|
||||||
amount: parseInt(invoice.amt_paid, 10),
|
|
||||||
coordinateHash: invoice.r_hash.toString('hex'),
|
|
||||||
coordinateIndex: parseInt(invoice.add_index, 10),
|
|
||||||
toLndPub: myLndPub,
|
|
||||||
inbound: true,
|
|
||||||
type: 'other', //TODO better this
|
|
||||||
fromGunPub: order.from,
|
|
||||||
toGunPub: myGunPub,
|
|
||||||
invoiceMemo: invoice.memo
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.on('data', invoiceSubCb)
|
|
||||||
|
|
||||||
stream.on('status', (/** @type {any} */ status) => {
|
|
||||||
logger.info(`${r_hash}, invoice status:`, status)
|
|
||||||
})
|
|
||||||
stream.on('end', () => {
|
|
||||||
logger.warn(`${r_hash}, invoice stream ended`)
|
|
||||||
})
|
|
||||||
stream.on('error', (/** @type {any} */ e) => {
|
|
||||||
logger.warn(`${r_hash}, error:`, e)
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`)
|
logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`)
|
||||||
|
|
||||||
const listenerEndTime = performance.now() - listenerStartTime
|
/**
|
||||||
|
*
|
||||||
|
* @type {Common.Schema.InvoiceWhenListed & {r_hash:Buffer,payment_addr:string}}
|
||||||
|
*/
|
||||||
|
const paidInvoice = await new Promise(res => {
|
||||||
|
SchemaManager.addListenInvoice(invoice.r_hash, res)
|
||||||
|
})
|
||||||
|
const hashString = paidInvoice.r_hash.toString('hex')
|
||||||
|
const {
|
||||||
|
amt_paid_sat: amt,
|
||||||
|
add_index: addIndex,
|
||||||
|
payment_addr: paymentAddr
|
||||||
|
} = paidInvoice
|
||||||
|
/**@type {'spontaneousPayment' | 'tip' | 'service' | 'product' | 'other'}*/
|
||||||
|
//@ts-expect-error to fix
|
||||||
|
const orderType = order.targetType
|
||||||
|
//@ts-expect-error to fix
|
||||||
|
const { ackInfo } = order //a string representing what has been requested
|
||||||
|
switch (orderType) {
|
||||||
|
case 'tip': {
|
||||||
|
if (!Common.isPopulatedString(ackInfo)) {
|
||||||
|
throw new Error('ackInfo for postID not a populated string')
|
||||||
|
} else {
|
||||||
|
getUser()
|
||||||
|
.get('postToTipCount')
|
||||||
|
.get(ackInfo)
|
||||||
|
.set(null) // each item in the set is a tip
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'spontaneousPayment': {
|
||||||
|
//no action required
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'product': {
|
||||||
|
//assuming digital product that only requires to be unlocked
|
||||||
|
const ackData = { productFinalRef: '' } //find ref by decrypting it base on "ackInfo" provided information
|
||||||
|
const toSend = JSON.stringify(ackData)
|
||||||
|
const encrypted = await SEA.encrypt(toSend, secret)
|
||||||
|
const ordResponse = {
|
||||||
|
type: 'orderAck',
|
||||||
|
content: encrypted
|
||||||
|
}
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
getUser()
|
||||||
|
.get(Key.ORDER_TO_RESPONSE)
|
||||||
|
.get(orderID)
|
||||||
|
.put(ordResponse, ack => {
|
||||||
|
if (ack.err && typeof ack.err !== 'number') {
|
||||||
|
rej(
|
||||||
|
new Error(
|
||||||
|
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'service': {
|
||||||
|
const ackData = { serviceFinalRef: '' } //find ref by decrypting it base on "ackInfo" provided information
|
||||||
|
const toSend = JSON.stringify(ackData)
|
||||||
|
const encrypted = await SEA.encrypt(toSend, secret)
|
||||||
|
const serviceResponse = {
|
||||||
|
type: 'orderAck',
|
||||||
|
content: encrypted
|
||||||
|
}
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
getUser()
|
||||||
|
.get(Key.ORDER_TO_RESPONSE)
|
||||||
|
.get(orderID)
|
||||||
|
.put(serviceResponse, ack => {
|
||||||
|
if (ack.err && typeof ack.err !== 'number') {
|
||||||
|
rej(
|
||||||
|
new Error(
|
||||||
|
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'other': //not implemented yet but save them as a coordinate anyways
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return //exit because not implemented
|
||||||
|
}
|
||||||
|
const myGunPub = getUser()._.sea.pub
|
||||||
|
SchemaManager.AddOrder({
|
||||||
|
type: orderType,
|
||||||
|
coordinateHash: hashString,
|
||||||
|
coordinateIndex: parseInt(addIndex, 10),
|
||||||
|
inbound: true,
|
||||||
|
amount: parseInt(amt, 10),
|
||||||
|
|
||||||
logger.info(`[PERF] Invoice generation completed in ${listenerEndTime}ms`)
|
toLndPub: paymentAddr,
|
||||||
|
fromGunPub: order.from,
|
||||||
|
toGunPub: myGunPub,
|
||||||
|
invoiceMemo: memo
|
||||||
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify(
|
`error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
const Crypto = require('crypto')
|
const Crypto = require('crypto')
|
||||||
const { Utils: CommonUtils } = require('shock-common')
|
const Common = require('shock-common')
|
||||||
const getGunUser = () => require('../gunDB/Mediator').getUser()
|
const getGunUser = () => require('../gunDB/Mediator').getUser()
|
||||||
const Key = require('../gunDB/contact-api/key')
|
const Key = require('../gunDB/contact-api/key')
|
||||||
/**
|
/**
|
||||||
|
|
@ -259,7 +259,7 @@ class SchemaManager {
|
||||||
if (!coordinates) {
|
if (!coordinates) {
|
||||||
return orders
|
return orders
|
||||||
}
|
}
|
||||||
await CommonUtils.asyncForEach(coordinates, async coordinateSHA256 => {
|
await Common.Utils.asyncForEach(coordinates, async coordinateSHA256 => {
|
||||||
const encryptedOrderString = await getGunUser()
|
const encryptedOrderString = await getGunUser()
|
||||||
.get(Key.COORDINATES)
|
.get(Key.COORDINATES)
|
||||||
.get(coordinateSHA256)
|
.get(coordinateSHA256)
|
||||||
|
|
@ -282,6 +282,52 @@ class SchemaManager {
|
||||||
return orderedOrders
|
return orderedOrders
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Common.Schema.InvoiceWhenListed & {r_hash:Buffer,payment_addr:string}} Invoice
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @type {Record<string,(invoice:Invoice) =>void>}
|
||||||
|
*/
|
||||||
|
_listeningInvoices = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Buffer} r_hash
|
||||||
|
* @param {(invoice:Invoice) =>void} done
|
||||||
|
*/
|
||||||
|
addListenInvoice(r_hash, done) {
|
||||||
|
const hashString = r_hash.toString("hex")
|
||||||
|
this._listeningInvoices[hashString] = done
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Common.Schema.InvoiceWhenListed & {r_hash:Buffer,payment_addr:string}} data
|
||||||
|
*/
|
||||||
|
invoiceStreamDataCb(data) {
|
||||||
|
if (!data.settled) {
|
||||||
|
//invoice not paid yet
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const hashString = data.r_hash.toString('hex')
|
||||||
|
const amt = parseInt(data.amt_paid_sat, 10)
|
||||||
|
if (this._listeningInvoices[hashString]) {
|
||||||
|
const done = this._listeningInvoices[hashString]
|
||||||
|
delete this._listeningInvoices[hashString]
|
||||||
|
done(data)
|
||||||
|
} else {
|
||||||
|
this.AddOrder({
|
||||||
|
type: 'invoice',
|
||||||
|
coordinateHash: hashString,
|
||||||
|
coordinateIndex: parseInt(data.add_index, 10),
|
||||||
|
inbound: true,
|
||||||
|
amount: amt,
|
||||||
|
toLndPub: data.payment_addr,
|
||||||
|
invoiceMemo: data.memo
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} address
|
* @param {string} address
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue