reduce duplication of invoicable code
This commit is contained in:
parent
b740eeb2c4
commit
32847670e2
|
@ -1,6 +1,6 @@
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { ensureProtocol, removeTracking } from '../../lib/url'
|
import { ensureProtocol, removeTracking } from '../../lib/url'
|
||||||
import serialize from './serial'
|
import { serializeInvoicable } from './serial'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
||||||
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
|
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
|
||||||
import domino from 'domino'
|
import domino from 'domino'
|
||||||
|
@ -16,8 +16,6 @@ import { advSchema, amountSchema, bountySchema, commentSchema, discussionSchema,
|
||||||
import { sendUserNotification } from '../webPush'
|
import { sendUserNotification } from '../webPush'
|
||||||
import { proxyImages } from './imgproxy'
|
import { proxyImages } from './imgproxy'
|
||||||
import { defaultCommentSort } from '../../lib/item'
|
import { defaultCommentSort } from '../../lib/item'
|
||||||
import { createHmac } from './wallet'
|
|
||||||
import { settleHodlInvoice } from 'ln-service'
|
|
||||||
|
|
||||||
export async function commentFilterClause (me, models) {
|
export async function commentFilterClause (me, models) {
|
||||||
let clause = ` AND ("Item"."weightedVotes" - "Item"."weightedDownVotes" > -${ITEM_FILTER_THRESHOLD}`
|
let clause = ` AND ("Item"."weightedVotes" - "Item"."weightedDownVotes" > -${ITEM_FILTER_THRESHOLD}`
|
||||||
|
@ -38,51 +36,6 @@ export async function commentFilterClause (me, models) {
|
||||||
return clause
|
return clause
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkInvoice (models, hash, hmac, fee) {
|
|
||||||
if (!hash) {
|
|
||||||
throw new GraphQLError('hash required', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
if (!hmac) {
|
|
||||||
throw new GraphQLError('hmac required', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
const hmac2 = createHmac(hash)
|
|
||||||
if (hmac !== hmac2) {
|
|
||||||
throw new GraphQLError('bad hmac', { extensions: { code: 'FORBIDDEN' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
const invoice = await models.invoice.findUnique({
|
|
||||||
where: { hash },
|
|
||||||
include: {
|
|
||||||
user: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!invoice) {
|
|
||||||
throw new GraphQLError('invoice not found', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
const expired = new Date(invoice.expiresAt) <= new Date()
|
|
||||||
if (expired) {
|
|
||||||
throw new GraphQLError('invoice expired', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
if (invoice.confirmedAt) {
|
|
||||||
throw new GraphQLError('invoice already used', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invoice.cancelled) {
|
|
||||||
throw new GraphQLError('invoice was canceled', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!invoice.msatsReceived) {
|
|
||||||
throw new GraphQLError('invoice was not paid', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
if (fee && msatsToSats(invoice.msatsReceived) < fee) {
|
|
||||||
throw new GraphQLError('invoice amount too low', { extensions: { code: 'BAD_INPUT' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
return invoice
|
|
||||||
}
|
|
||||||
|
|
||||||
async function comments (me, models, id, sort) {
|
async function comments (me, models, id, sort) {
|
||||||
let orderBy
|
let orderBy
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
|
@ -714,72 +667,45 @@ export default {
|
||||||
return rItem
|
return rItem
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pollVote: async (parent, { id, hash, hmac }, { me, models }) => {
|
pollVote: async (parent, { id, hash, hmac }, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
let invoice
|
await serializeInvoicable(
|
||||||
if (hash) {
|
models.$queryRawUnsafe(`${SELECT} FROM poll_vote($1::INTEGER, $2::INTEGER) AS "Item"`, Number(id), Number(me.id)),
|
||||||
invoice = await checkInvoice(models, hash, hmac)
|
{ me, models, lnd, hash, hmac }
|
||||||
}
|
)
|
||||||
|
|
||||||
const trx = [
|
|
||||||
models.$queryRawUnsafe(`${SELECT} FROM poll_vote($1::INTEGER, $2::INTEGER) AS "Item"`, Number(id), Number(me.id))
|
|
||||||
]
|
|
||||||
if (invoice) {
|
|
||||||
trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`)
|
|
||||||
trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
|
|
||||||
}
|
|
||||||
|
|
||||||
await serialize(models, ...trx)
|
|
||||||
|
|
||||||
return id
|
return id
|
||||||
},
|
},
|
||||||
act: async (parent, { id, sats, hash, hmac }, { me, models, lnd }) => {
|
act: async (parent, { id, sats, hash, hmac }, { me, models, lnd }) => {
|
||||||
// need to make sure we are logged in
|
|
||||||
if (!me && !hash) {
|
|
||||||
throw new GraphQLError('you must be logged in or pay', { extensions: { code: 'FORBIDDEN' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
await ssValidate(amountSchema, { amount: sats })
|
await ssValidate(amountSchema, { amount: sats })
|
||||||
|
|
||||||
let user = me
|
|
||||||
let invoice
|
|
||||||
if (hash) {
|
|
||||||
invoice = await checkInvoice(models, hash, hmac, sats)
|
|
||||||
if (!me) user = invoice.user
|
|
||||||
}
|
|
||||||
|
|
||||||
// disallow self tips except anons
|
// disallow self tips except anons
|
||||||
if (user.id !== ANON_USER_ID) {
|
if (me) {
|
||||||
const [item] = await models.$queryRawUnsafe(`
|
const [item] = await models.$queryRawUnsafe(`
|
||||||
${SELECT}
|
${SELECT}
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
WHERE id = $1 AND "userId" = $2`, Number(id), user.id)
|
WHERE id = $1 AND "userId" = $2`, Number(id), me.id)
|
||||||
if (item) {
|
if (item) {
|
||||||
throw new GraphQLError('cannot zap your self', { extensions: { code: 'BAD_INPUT' } })
|
throw new GraphQLError('cannot zap your self', { extensions: { code: 'BAD_INPUT' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disallow tips if me is one of the forward user recipients
|
||||||
|
const existingForwards = await models.itemForward.findMany({ where: { itemId: Number(id) } })
|
||||||
|
if (existingForwards.some(fwd => Number(fwd.userId) === Number(me.id))) {
|
||||||
|
throw new GraphQLError('cannot zap a post for which you are forwarded zaps', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disallow tips if me is one of the forward user recipients
|
const { item_act: vote } = await serializeInvoicable(
|
||||||
const existingForwards = await models.itemForward.findMany({ where: { itemId: Number(id) } })
|
models.$queryRaw`
|
||||||
if (existingForwards.some(fwd => Number(fwd.userId) === Number(user.id))) {
|
SELECT
|
||||||
throw new GraphQLError('cannot zap a post for which you are forwarded zaps', { extensions: { code: 'BAD_INPUT' } })
|
item_act(${Number(id)}::INTEGER,
|
||||||
}
|
${me?.id || ANON_USER_ID}::INTEGER, 'TIP', ${Number(sats)}::INTEGER)`,
|
||||||
|
{ me, models, lnd, hash, hmac, enforceFee: sats }
|
||||||
const trx = [
|
)
|
||||||
models.$queryRaw`SELECT item_act(${Number(id)}::INTEGER, ${user.id}::INTEGER, 'TIP', ${Number(sats)}::INTEGER)`
|
|
||||||
]
|
|
||||||
if (invoice) {
|
|
||||||
trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`)
|
|
||||||
trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = await serialize(models, ...trx)
|
|
||||||
const { item_act: vote } = trx.length > 1 ? query[1][0] : query[0]
|
|
||||||
|
|
||||||
if (invoice?.isHeld) await settleHodlInvoice({ secret: invoice.preimage, lnd })
|
|
||||||
|
|
||||||
const notify = async () => {
|
const notify = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -837,17 +763,12 @@ export default {
|
||||||
sats
|
sats
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dontLikeThis: async (parent, { id, sats, hash, hmac }, { me, models }) => {
|
dontLikeThis: async (parent, { id, sats = DONT_LIKE_THIS_COST, hash, hmac }, { me, lnd, models }) => {
|
||||||
// need to make sure we are logged in
|
// need to make sure we are logged in
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
let invoice
|
|
||||||
if (hash) {
|
|
||||||
invoice = await checkInvoice(models, hash, hmac, DONT_LIKE_THIS_COST)
|
|
||||||
}
|
|
||||||
|
|
||||||
// disallow self down votes
|
// disallow self down votes
|
||||||
const [item] = await models.$queryRawUnsafe(`
|
const [item] = await models.$queryRawUnsafe(`
|
||||||
${SELECT}
|
${SELECT}
|
||||||
|
@ -857,16 +778,11 @@ export default {
|
||||||
throw new GraphQLError('cannot downvote your self', { extensions: { code: 'BAD_INPUT' } })
|
throw new GraphQLError('cannot downvote your self', { extensions: { code: 'BAD_INPUT' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const trx = [
|
await serializeInvoicable(
|
||||||
models.$queryRaw`SELECT item_act(${Number(id)}::INTEGER,
|
models.$queryRaw`SELECT item_act(${Number(id)}::INTEGER,
|
||||||
${me.id}::INTEGER, 'DONT_LIKE_THIS', ${sats || DONT_LIKE_THIS_COST}::INTEGER)`
|
${me.id}::INTEGER, 'DONT_LIKE_THIS', ${sats}::INTEGER)`,
|
||||||
]
|
{ me, models, lnd, hash, hmac }
|
||||||
if (invoice) {
|
)
|
||||||
trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`)
|
|
||||||
trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
|
|
||||||
}
|
|
||||||
|
|
||||||
await serialize(models, ...trx)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1126,6 +1042,7 @@ export const updateItem = async (parent, { sub: subName, forward, options, ...it
|
||||||
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in case they lied about their existing boost
|
||||||
await ssValidate(advSchema, { boost: item.boost }, { models, me, existingBoost: old.boost })
|
await ssValidate(advSchema, { boost: item.boost }, { models, me, existingBoost: old.boost })
|
||||||
|
|
||||||
// if it's not the FAQ, not their bio, and older than 10 minutes
|
// if it's not the FAQ, not their bio, and older than 10 minutes
|
||||||
|
@ -1157,29 +1074,16 @@ export const updateItem = async (parent, { sub: subName, forward, options, ...it
|
||||||
item = { subName, userId: me.id, ...item }
|
item = { subName, userId: me.id, ...item }
|
||||||
const fwdUsers = await getForwardUsers(models, forward)
|
const fwdUsers = await getForwardUsers(models, forward)
|
||||||
|
|
||||||
let invoice
|
item = await serializeInvoicable(
|
||||||
if (hash) {
|
|
||||||
invoice = await checkInvoice(models, hash, hmac)
|
|
||||||
}
|
|
||||||
|
|
||||||
const trx = [
|
|
||||||
models.$queryRawUnsafe(`${SELECT} FROM update_item($1::JSONB, $2::JSONB, $3::JSONB) AS "Item"`,
|
models.$queryRawUnsafe(`${SELECT} FROM update_item($1::JSONB, $2::JSONB, $3::JSONB) AS "Item"`,
|
||||||
JSON.stringify(item), JSON.stringify(fwdUsers), JSON.stringify(options))
|
JSON.stringify(item), JSON.stringify(fwdUsers), JSON.stringify(options)),
|
||||||
]
|
{ models, lnd, hash, hmac, me }
|
||||||
if (invoice) {
|
)
|
||||||
trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`)
|
|
||||||
trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = await serialize(models, ...trx)
|
await createMentions(item, models)
|
||||||
const rItem = trx.length > 1 ? query[1][0] : query[0]
|
|
||||||
|
|
||||||
if (invoice?.isHeld) await settleHodlInvoice({ secret: invoice.preimage, lnd })
|
item.comments = []
|
||||||
|
return item
|
||||||
await createMentions(rItem, models)
|
|
||||||
|
|
||||||
rItem.comments = []
|
|
||||||
return rItem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createItem = async (parent, { forward, options, ...item }, { me, models, lnd, hash, hmac }) => {
|
export const createItem = async (parent, { forward, options, ...item }, { me, models, lnd, hash, hmac }) => {
|
||||||
|
@ -1189,21 +1093,7 @@ export const createItem = async (parent, { forward, options, ...item }, { me, mo
|
||||||
item.subName = item.sub
|
item.subName = item.sub
|
||||||
delete item.sub
|
delete item.sub
|
||||||
|
|
||||||
if (!me && !hash) {
|
item.userId = me ? Number(me.id) : ANON_USER_ID
|
||||||
throw new GraphQLError('you must be logged in or pay', { extensions: { code: 'FORBIDDEN' } })
|
|
||||||
}
|
|
||||||
let invoice
|
|
||||||
if (hash) {
|
|
||||||
// if we are logged in, we don't compare the invoice amount with the fee
|
|
||||||
// since it's not a fixed amount that we could use here.
|
|
||||||
// we rely on the query telling us if the balance is too low
|
|
||||||
const fee = !me ? (item.parentId ? ANON_COMMENT_FEE : ANON_POST_FEE) : undefined
|
|
||||||
invoice = await checkInvoice(models, hash, hmac, fee)
|
|
||||||
item.userId = invoice.user.id
|
|
||||||
}
|
|
||||||
if (me) {
|
|
||||||
item.userId = Number(me.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fwdUsers = await getForwardUsers(models, forward)
|
const fwdUsers = await getForwardUsers(models, forward)
|
||||||
if (item.text) {
|
if (item.text) {
|
||||||
|
@ -1215,19 +1105,13 @@ export const createItem = async (parent, { forward, options, ...item }, { me, mo
|
||||||
item.url = await proxyImages(item.url)
|
item.url = await proxyImages(item.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
const trx = [
|
const enforceFee = me ? undefined : (item.parentId ? ANON_COMMENT_FEE : (ANON_POST_FEE + (item.boost || 0)))
|
||||||
models.$queryRawUnsafe(`${SELECT} FROM create_item($1::JSONB, $2::JSONB, $3::JSONB, '${spamInterval}'::INTERVAL) AS "Item"`,
|
item = await serializeInvoicable(
|
||||||
JSON.stringify(item), JSON.stringify(fwdUsers), JSON.stringify(options))
|
models.$queryRawUnsafe(
|
||||||
]
|
`${SELECT} FROM create_item($1::JSONB, $2::JSONB, $3::JSONB, '${spamInterval}'::INTERVAL) AS "Item"`,
|
||||||
if (invoice) {
|
JSON.stringify(item), JSON.stringify(fwdUsers), JSON.stringify(options)),
|
||||||
trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`)
|
{ models, lnd, hash, hmac, me, enforceFee }
|
||||||
trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
|
)
|
||||||
}
|
|
||||||
|
|
||||||
const query = await serialize(models, ...trx)
|
|
||||||
item = trx.length > 1 ? query[1][0] : query[0]
|
|
||||||
|
|
||||||
if (invoice?.isHeld) await settleHodlInvoice({ secret: invoice.preimage, lnd })
|
|
||||||
|
|
||||||
await createMentions(item, models)
|
await createMentions(item, models)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { settleHodlInvoice } from 'ln-service'
|
|
||||||
import { amountSchema, ssValidate } from '../../lib/validate'
|
import { amountSchema, ssValidate } from '../../lib/validate'
|
||||||
import serialize from './serial'
|
import { serializeInvoicable } from './serial'
|
||||||
import { ANON_USER_ID } from '../../lib/constants'
|
import { ANON_USER_ID } from '../../lib/constants'
|
||||||
import { getItem, checkInvoice } from './item'
|
import { getItem } from './item'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
|
@ -104,37 +103,12 @@ export default {
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
donateToRewards: async (parent, { sats, hash, hmac }, { me, models, lnd }) => {
|
donateToRewards: async (parent, { sats, hash, hmac }, { me, models, lnd }) => {
|
||||||
if (!me && !hash) {
|
|
||||||
throw new GraphQLError('you must be logged in or pay', { extensions: { code: 'FORBIDDEN' } })
|
|
||||||
}
|
|
||||||
let user
|
|
||||||
if (me) {
|
|
||||||
user = me
|
|
||||||
}
|
|
||||||
|
|
||||||
let invoice
|
|
||||||
if (hash) {
|
|
||||||
invoice = await checkInvoice(models, hash, hmac, sats)
|
|
||||||
user = invoice.user
|
|
||||||
}
|
|
||||||
|
|
||||||
await ssValidate(amountSchema, { amount: sats })
|
await ssValidate(amountSchema, { amount: sats })
|
||||||
|
|
||||||
const trx = [
|
await serializeInvoicable(
|
||||||
models.$queryRawUnsafe(
|
models.$queryRaw`SELECT donate(${sats}::INTEGER, ${me?.id || ANON_USER_ID}::INTEGER)`,
|
||||||
'SELECT donate($1::INTEGER, $2::INTEGER)',
|
{ models, lnd, hash, hmac, me, enforceFee: sats }
|
||||||
sats, Number(user.id))
|
)
|
||||||
]
|
|
||||||
|
|
||||||
if (invoice) {
|
|
||||||
trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${user.id}`)
|
|
||||||
trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
|
|
||||||
}
|
|
||||||
await serialize(models, ...trx)
|
|
||||||
|
|
||||||
if (invoice?.isHeld) {
|
|
||||||
await settleHodlInvoice({ secret: invoice.preimage, lnd })
|
|
||||||
}
|
|
||||||
|
|
||||||
return sats
|
return sats
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import retry from 'async-retry'
|
import retry from 'async-retry'
|
||||||
import Prisma from '@prisma/client'
|
import Prisma from '@prisma/client'
|
||||||
|
import { settleHodlInvoice } from 'ln-service'
|
||||||
|
import { createHmac } from './wallet'
|
||||||
|
import { msatsToSats } from '../../lib/format'
|
||||||
|
|
||||||
async function serialize (models, ...calls) {
|
export default async function serialize (models, ...calls) {
|
||||||
return await retry(async bail => {
|
return await retry(async bail => {
|
||||||
try {
|
try {
|
||||||
const [, ...result] = await models.$transaction(
|
const [, ...result] = await models.$transaction(
|
||||||
|
@ -56,4 +59,72 @@ async function serialize (models, ...calls) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default serialize
|
export async function serializeInvoicable (query, { models, lnd, hash, hmac, me, enforceFee }) {
|
||||||
|
if (!me && !hash) {
|
||||||
|
throw new Error('you must be logged in or pay')
|
||||||
|
}
|
||||||
|
|
||||||
|
let trx = [query]
|
||||||
|
|
||||||
|
let invoice
|
||||||
|
if (hash) {
|
||||||
|
invoice = await checkInvoice(models, hash, hmac, enforceFee)
|
||||||
|
trx = [
|
||||||
|
models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`,
|
||||||
|
query,
|
||||||
|
models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } })
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await serialize(models, ...trx)
|
||||||
|
const result = trx.length > 1 ? results[1][0] : results[0]
|
||||||
|
|
||||||
|
if (invoice?.isHeld) await settleHodlInvoice({ secret: invoice.preimage, lnd })
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkInvoice (models, hash, hmac, fee) {
|
||||||
|
if (!hash) {
|
||||||
|
throw new GraphQLError('hash required', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
if (!hmac) {
|
||||||
|
throw new GraphQLError('hmac required', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
const hmac2 = createHmac(hash)
|
||||||
|
if (hmac !== hmac2) {
|
||||||
|
throw new GraphQLError('bad hmac', { extensions: { code: 'FORBIDDEN' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoice = await models.invoice.findUnique({
|
||||||
|
where: { hash },
|
||||||
|
include: {
|
||||||
|
user: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!invoice) {
|
||||||
|
throw new GraphQLError('invoice not found', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const expired = new Date(invoice.expiresAt) <= new Date()
|
||||||
|
if (expired) {
|
||||||
|
throw new GraphQLError('invoice expired', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
if (invoice.confirmedAt) {
|
||||||
|
throw new GraphQLError('invoice already used', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invoice.cancelled) {
|
||||||
|
throw new GraphQLError('invoice was canceled', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!invoice.msatsReceived) {
|
||||||
|
throw new GraphQLError('invoice was not paid', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
if (fee && msatsToSats(invoice.msatsReceived) < fee) {
|
||||||
|
throw new GraphQLError('invoice amount too low', { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoice
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue