support NIP-57
This commit is contained in:
parent
558419ffb3
commit
ef5346000b
|
@ -1534,6 +1534,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@noble/secp256k1": {
|
||||||
|
"version": "1.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
|
||||||
|
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw=="
|
||||||
|
},
|
||||||
"@opensearch-project/opensearch": {
|
"@opensearch-project/opensearch": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-1.1.0.tgz",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.7.1",
|
"@apollo/client": "^3.7.1",
|
||||||
"@lexical/react": "^0.7.5",
|
"@lexical/react": "^0.7.5",
|
||||||
|
"@noble/secp256k1": "^1.7.1",
|
||||||
"@opensearch-project/opensearch": "^1.1.0",
|
"@opensearch-project/opensearch": "^1.1.0",
|
||||||
"@prisma/client": "^2.30.3",
|
"@prisma/client": "^2.30.3",
|
||||||
"@synonymdev/slashtags-auth": "^1.0.0-alpha.5",
|
"@synonymdev/slashtags-auth": "^1.0.0-alpha.5",
|
||||||
|
|
|
@ -7,11 +7,20 @@ export default async ({ query: { username } }, res) => {
|
||||||
return res.status(400).json({ status: 'ERROR', reason: `user @${username} does not exist` })
|
return res.status(400).json({ status: 'ERROR', reason: `user @${username} does not exist` })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nostr = {}
|
||||||
|
if (user.nostrPubkey) {
|
||||||
|
nostr = {
|
||||||
|
nostrPubkey: user.nostrPubkey,
|
||||||
|
allowsNostr: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
callback: `${process.env.PUBLIC_URL}/api/lnurlp/${username}/pay`, // The URL from LN SERVICE which will accept the pay request parameters
|
callback: `${process.env.PUBLIC_URL}/api/lnurlp/${username}/pay`, // The URL from LN SERVICE which will accept the pay request parameters
|
||||||
minSendable: 1000, // Min amount LN SERVICE is willing to receive, can not be less than 1 or more than `maxSendable`
|
minSendable: 1000, // Min amount LN SERVICE is willing to receive, can not be less than 1 or more than `maxSendable`
|
||||||
maxSendable: 1000000000,
|
maxSendable: 1000000000,
|
||||||
metadata: lnurlPayMetadataString(username), // Metadata json which must be presented as raw string here, this is required to pass signature verification at a later step
|
metadata: lnurlPayMetadataString(username), // Metadata json which must be presented as raw string here, this is required to pass signature verification at a later step
|
||||||
tag: 'payRequest' // Type of LNURL
|
tag: 'payRequest', // Type of LNURL
|
||||||
|
...nostr
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,34 @@ import lnd from '../../../../api/lnd'
|
||||||
import { createInvoice } from 'ln-service'
|
import { createInvoice } from 'ln-service'
|
||||||
import { lnurlPayDescriptionHashForUser } from '../../../../lib/lnurl'
|
import { lnurlPayDescriptionHashForUser } from '../../../../lib/lnurl'
|
||||||
import serialize from '../../../../api/resolvers/serial'
|
import serialize from '../../../../api/resolvers/serial'
|
||||||
|
import * as secp256k1 from '@noble/secp256k1'
|
||||||
|
import { createHash } from 'crypto'
|
||||||
|
|
||||||
export default async ({ query: { username, amount } }, res) => {
|
export default async ({ query: { username, amount, nostr } }, res) => {
|
||||||
const user = await models.user.findUnique({ where: { name: username } })
|
const user = await models.user.findUnique({ where: { name: username } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(400).json({ status: 'ERROR', reason: `user @${username} does not exist` })
|
return res.status(400).json({ status: 'ERROR', reason: `user @${username} does not exist` })
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
// if nostr, decode, validate sig, check tags, set description hash
|
||||||
|
let description, descriptionHash
|
||||||
|
if (nostr) {
|
||||||
|
const noteStr = decodeURI(nostr)
|
||||||
|
const note = JSON.parse(noteStr)
|
||||||
|
const hasPTag = note.tags?.filter(t => t[0] === 'p').length >= 1
|
||||||
|
const hasETag = note.tags?.filter(t => t[0] === 'e').length <= 1
|
||||||
|
if (await secp256k1.schnorr.verify(note.sig, note.id, note.pubkey) &&
|
||||||
|
hasPTag && hasETag) {
|
||||||
|
description = user.hideInvoiceDesc ? undefined : `${amount} msats for @${user.name} on stacker.news via NIP-57`
|
||||||
|
descriptionHash = createHash('sha256').update(noteStr).digest('hex')
|
||||||
|
} else {
|
||||||
|
res.status(400).json({ status: 'ERROR', reason: 'invalid NIP-57 note' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
description = user.hideInvoiceDesc ? undefined : `${amount} msats for @${user.name} on stacker.news`
|
||||||
|
descriptionHash = lnurlPayDescriptionHashForUser(username)
|
||||||
|
}
|
||||||
|
|
||||||
if (!amount || amount < 1000) {
|
if (!amount || amount < 1000) {
|
||||||
return res.status(400).json({ status: 'ERROR', reason: 'amount must be >=1000 msats' })
|
return res.status(400).json({ status: 'ERROR', reason: 'amount must be >=1000 msats' })
|
||||||
|
@ -16,11 +38,8 @@ export default async ({ query: { username, amount } }, res) => {
|
||||||
|
|
||||||
// generate invoice
|
// generate invoice
|
||||||
const expiresAt = new Date(new Date().setMinutes(new Date().getMinutes() + 1))
|
const expiresAt = new Date(new Date().setMinutes(new Date().getMinutes() + 1))
|
||||||
const description = `${amount} msats for @${user.name} on stacker.news`
|
|
||||||
const descriptionHash = lnurlPayDescriptionHashForUser(username)
|
|
||||||
try {
|
|
||||||
const invoice = await createInvoice({
|
const invoice = await createInvoice({
|
||||||
description: user.hideInvoiceDesc ? undefined : description,
|
description: description,
|
||||||
description_hash: descriptionHash,
|
description_hash: descriptionHash,
|
||||||
lnd,
|
lnd,
|
||||||
mtokens: amount,
|
mtokens: amount,
|
||||||
|
|
Loading…
Reference in New Issue