diff --git a/src/routes.js b/src/routes.js index 7febbac8..998f983e 100644 --- a/src/routes.js +++ b/src/routes.js @@ -14,7 +14,7 @@ const Common = require('shock-common') const isARealUsableNumber = require('lodash/isFinite') const Big = require('big.js') const size = require('lodash/size') -const { range, flatten } = require('ramda') +const { range, flatten, evolve } = require('ramda') const getListPage = require('../utils/paginate') const auth = require('../services/auth/auth') @@ -31,7 +31,8 @@ const GunGetters = require('../services/gunDB/contact-api/getters') const GunKey = require('../services/gunDB/contact-api/key') const { sendPaymentV2Keysend, - sendPaymentV2Invoice + sendPaymentV2Invoice, + listPayments } = require('../utils/lightningServices/v2') const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10 @@ -1238,6 +1239,60 @@ module.exports = async ( ) }) + app.get('/api/lnd/payments', async (req, res) => { + const { + include_incomplete, + index_offset, + max_payments, + reversed + } = /** @type {Common.APISchema.ListPaymentsRequest} */ (evolve( + { + include_incomplete: x => x === 'true', + index_offset: x => Number(x), + max_payments: x => Number(x), + reversed: x => x === 'true' + }, + req.query + )) + + if (typeof include_incomplete !== 'boolean') { + return res.status(400).json({ + field: 'include_incomplete', + errorMessage: 'include_incomplete not a boolean' + }) + } + + if (!isARealUsableNumber(index_offset)) { + return res.status(400).json({ + field: 'index_offset', + errorMessage: 'index_offset not a number' + }) + } + + if (!isARealUsableNumber(max_payments)) { + return res.status(400).json({ + field: 'max_payments', + errorMessage: 'max_payments not a number' + }) + } + + if (typeof reversed !== 'boolean') { + return res.status(400).json({ + field: 'reversed', + errorMessage: 'reversed not a boolean' + }) + } + + return res.status(200).json( + await listPayments({ + include_incomplete, + index_offset, + max_payments, + reversed + }) + ) + }) + // get lnd node invoices list app.get('/api/lnd/listinvoices', (req, res) => { const { lightning } = LightningServices.services diff --git a/utils/lightningServices/v2.js b/utils/lightningServices/v2.js index 18bf4fef..0f6a19e3 100644 --- a/utils/lightningServices/v2.js +++ b/utils/lightningServices/v2.js @@ -3,6 +3,8 @@ */ const Crypto = require('crypto') const logger = require('winston') +const Common = require('shock-common') +const Ramda = require('ramda') const lightningServices = require('./lightning-services') /** @@ -337,7 +339,50 @@ const sendPaymentV2Invoice = params => { }) } +/** + * @param {Common.APISchema.ListPaymentsRequest} req + * @throws {TypeError} + * @returns {Promise} + */ +const listPayments = req => { + return Common.Utils.makePromise((res, rej) => { + lightningServices.lightning.listPayments( + req, + /** + * @param {{ details: any; }} err + * @param {unknown} lpres + */ (err, lpres) => { + if (err) { + return rej(new Error(err.details || err)) + } + + if (!Common.APISchema.isListPaymentsResponse(lpres)) { + return rej(new TypeError(`Response from LND not in expected format.`)) + } + + /** @type {Common.APISchema.ListPaymentsResponseParsed} */ + // @ts-expect-error + const parsed = Ramda.evolve( + { + first_index_offset: x => Number(x), + last_index_offset: x => Number(x), + payments: x => x + }, + lpres + ) + + if (Common.APISchema.isListPaymentsResponseParsed(parsed)) { + return res(parsed) + } + + return rej(new TypeError(`could not parse response from LND`)) + } + ) + }) +} + module.exports = { sendPaymentV2Keysend, - sendPaymentV2Invoice + sendPaymentV2Invoice, + listPayments }