Skip to content

Commit 89963b2

Browse files
author
fiatjaf
committed
address (most) comments on #78
1 parent 443940d commit 89963b2

File tree

5 files changed

+68
-37
lines changed

5 files changed

+68
-37
lines changed

bin/charged

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const args = require('meow')(`
1616
-e, --node-env <env> nodejs environment mode [default: production]
1717
--allow-cors <origin> allow browser CORS requests from <origin> [default: off]
1818
19+
--url <url> sets the base URL from which public pages will be served [default: use a relative URL]
1920
--rate-proxy <uri> proxy to use for fetching exchange rate from bitstamp/coingecko [default: see proxy-from-env]
2021
--hook-proxy <uri> proxy to use for web hook push requests [default: see proxy-from-env]
2122
--all-proxy <uri> proxy to use for all http requests [default: see proxy-from-env]

migrations/20201101170804_lnurlpay.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ exports.up = async db => {
77
t.string('currency').nullable()
88
t.string('text').notNullable()
99
t.string('image').nullable()
10+
t.string('other_metadata').nullable()
1011
t.string('success_text').nullable()
1112
t.string('success_secret').nullable()
1213
t.string('success_url').nullable()
13-
t.integer('comment').notNullable().defaultTo(0)
14+
t.integer('comment_length').notNullable().defaultTo(0)
1415
t.string('webhook').nullable()
1516
})
1617
await db.schema.table('invoice', t => {

src/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const lnPath = process.env.LN_PATH || join(require('os').homedir(), '.lightnin
3737

3838
require('./invoicing')(app, payListen, model, auth, lnconf)
3939
require('./checkout')(app, payListen)
40-
require('./lnurl')(app, payListen, model, auth)
40+
require('./lnurl')(app, payListen, model, auth, ln)
4141

4242
require('./sse')(app, payListen, auth)
4343
require('./webhook')(app, payListen, model, auth)

src/lnurl.js

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,19 @@ import wrap from './lib/promise-wrap'
33

44
const debug = require('debug')('lightning-charge')
55

6-
module.exports = (app, payListen, model, auth) => {
6+
module.exports = (app, payListen, model, auth, ln) => async {
7+
// check if method invoicewithdescriptionhash exists
8+
let help = await ln.help()
9+
let foundCommand
10+
for (let i = 0; i < help.help.length; i++) {
11+
let command = help.help[i].command
12+
if (command.slice(0, 26) !== 'invoicewithdescriptionhash') continue
13+
foundCommand = true
14+
break
15+
}
16+
if (!foundCommand) return
17+
18+
// define routes
719
const {
820
newInvoice, listInvoicesByLnurlPayEndpoint
921
, getLnurlPayEndpoint, listLnurlPayEndpoints
@@ -26,47 +38,65 @@ module.exports = (app, payListen, model, auth) => {
2638
addBech32Lnurl(req, await setLnurlPayEndpoint(req.params.id, req.body))
2739
)))
2840

29-
app.delete('/endpoint/:id', auth, wrap(async (req, res) =>
30-
res.status(200).send(await delLnurlPayEndpoint(req.params.id))))
41+
app.delete('/endpoint/:id', auth, wrap(async (req, res) => {
42+
const deletedRows = await delLnurlPayEndpoint(req.params.id)
43+
if (deletedRows) res.status(204)
44+
else res.status(404)
45+
}))
3146

32-
app.get('/endpoint/:id', auth, wrap(async (req, res) =>
33-
res.status(200).send(
34-
addBech32Lnurl(req, await getLnurlPayEndpoint(req.params.id))
35-
)))
47+
app.get('/endpoint/:id', auth, wrap(async (req, res) => {
48+
const endpoint = await getLnurlPayEndpoint(req.params.id)
49+
if (endpoint) res.status(200).send(addBech32Lnurl(req, endpoint))
50+
else res.status(404)
51+
}))
3652

3753
app.get('/endpoint/:id/invoices', auth, wrap(async (req, res) =>
3854
res.send(await listInvoicesByLnurlPayEndpoint(req.params.id))))
3955

4056
// this is the actual endpoint users will hit
4157
app.get('/lnurl/:id', wrap(async (req, res) => {
42-
const lnurlpay = await getLnurlPayEndpoint(req.params.id)
58+
const endpoint = await getLnurlPayEndpoint(req.params.id)
59+
60+
if (!endpoint) {
61+
res.status(404)
62+
return
63+
}
4364

4465
res.status(200).send({
4566
tag: 'payRequest'
46-
, minSendable: lnurlpay.min
47-
, maxSendable: lnurlpay.max
48-
, metadata: makeMetadata(lnurlpay)
49-
, commentAllowed: lnurlpay.comment
67+
, minSendable: endpoint.min
68+
, maxSendable: endpoint.max
69+
, metadata: makeMetadata(endpoint)
70+
, commentAllowed: endpoint.comment_length
5071
, callback: `https://${req.hostname}/lnurl/${lnurlpay.id}/callback`
5172
})
5273
}))
5374

5475
app.get('/lnurl/:id/callback', wrap(async (req, res) => {
55-
const lnurlpay = await getLnurlPayEndpoint(req.params.id)
76+
const endpoint = await getLnurlPayEndpoint(req.params.id)
77+
const amount = +req.query.amount
5678

57-
if (req.query.amount > lnurlpay.max)
58-
return res.send({status: 'ERROR', reason: 'amount too large'})
59-
if (req.query.amount < lnurlpay.min)
60-
return res.send({status: 'ERROR', reason: 'amount too small'})
79+
if (!amount)
80+
return res.send({status: 'ERROR', reason: `invalid amount '${req.query.amount}'`})
81+
if (amount > endpoint.max)
82+
return res.send({status: 'ERROR', reason: `amount must be smaller than ${Math.floor(endpoint.max / 1000)} sat`})
83+
if (amount < endpoint.min)
84+
return res.send({status: 'ERROR', reason: `amount must be greater than ${Math.ceil(endpoint.min / 1000)} sat`})
6185

6286
let invoiceMetadata = {...req.query}
6387
delete invoiceMetadata.amount
6488
delete invoiceMetadata.fromnodes
6589
delete invoiceMetadata.nonce
66-
invoiceMetadata = {...lnurlpay.metadata, ...invoiceMetadata}
90+
invoiceMetadata = {...endpoint.metadata, ...invoiceMetadata}
91+
92+
// enforce comment length
93+
invoiceMetadata.comment =
94+
(lnurlpay.comment && req.query.comment)
95+
? (''+req.query.comment).substr(0, lnurlpay.comment)
96+
: undefined
6797

6898
const invoice = await newInvoice({
69-
descriptionHash: require('crypto')
99+
description_hash: require('crypto')
70100
.createHash('sha256')
71101
.update(makeMetadata(lnurlpay))
72102
.digest('hex')
@@ -83,7 +113,7 @@ module.exports = (app, payListen, model, auth) => {
83113
, url: lnurlpay.success_url
84114
, description: lnurlpay.success_text || ''
85115
}
86-
} else if (lnurlpay.success_value) {
116+
} else if (lnurlpay.success_secret) {
87117
// not implemented yet
88118
} else if (lnurlpay.success_text) {
89119
successAction = {tag: 'message', message: lnurlpay.success_text}
@@ -98,18 +128,18 @@ module.exports = (app, payListen, model, auth) => {
98128
}))
99129
}
100130

101-
function makeMetadata (lnurlpay) {
102-
const text = lnurlpay.text
103-
104-
const meta = [['text/plain', text]]
105-
.concat(lnurlpay.image ? ['image/png;base64', lnurlpay.image] : [])
106-
107-
return JSON.stringify(meta)
131+
function makeMetadata (endpoint) {
132+
return JSON.stringify(
133+
[['text/plain', endpoint.text]]
134+
.concat(endpoint.image ? ['image/png;base64', endpoint.image] : [])
135+
.concat(JSON.parse(endpoint.other_metadata || []))
136+
)
108137
}
109138

110139
function addBech32Lnurl (req, lnurlpay) {
111-
const hostname = req.hostname || req.params.hostname
112-
const url = `https://${hostname}/lnurl/${lnurlpay.id}`
140+
let base = process.env.URL || `https://${req.hostname}`
141+
base = base[base.length - 1] === '/' ? base.slice(0, -1) : base
142+
const url = `${base}/lnurl/${lnurlpay.id}`
113143
const words = bech32.toWords(Buffer.from(url))
114144
lnurlpay.bech32 = bech32.encode('lnurl', words, 2500).toUpperCase()
115145
return lnurlpay

src/model.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ module.exports = (db, ln) => {
2121

2222
const id = nanoid()
2323
, msatoshi = props.msatoshi ? ''+props.msatoshi : currency ? await toMsat(currency, amount) : ''
24-
, desc = props.descriptionHash || (props.description ? ''+props.description : defaultDesc)
25-
, method = props.descriptionHash ? 'invoicewithdescriptionhash' : 'invoice'
24+
, desc = props.description_hash || (props.description ? ''+props.description : defaultDesc)
25+
, method = props.description_hash ? 'invoicewithdescriptionhash' : 'invoice'
2626
, lninv = await ln.call(method, [msatoshi || 'any', id, desc, expiry])
2727

2828
const invoice = {
@@ -63,17 +63,16 @@ module.exports = (db, ln) => {
6363
.then(rows => rows.map(format))
6464

6565
const getLnurlPayEndpoint = async id => {
66-
let lnurlpay = await db('lnurlpay_endpoint').where({ id }).first()
67-
return formatLnurlpay(lnurlpay)
66+
let endpoint = await db('lnurlpay_endpoint').where({ id }).first()
67+
return endpoint && formatLnurlpay(endpoint)
6868
}
6969

7070
const setLnurlPayEndpoint = async (id, props) => {
7171
let lnurlpay
7272
if (id) {
7373
lnurlpay = await db('lnurlpay_endpoint').where({ id }).first()
7474
lnurlpay = { ...lnurlpay, ...props }
75-
} else
76-
lnurlpay = { ...props, id: nanoid() }
75+
} else lnurlpay = { ...props, id: nanoid() }
7776

7877
if (typeof props.metadata != 'undefined') {
7978
let metadata = JSON.stringify(props.metadata || {})

0 commit comments

Comments
 (0)