Skip to content

Commit f237400

Browse files
authored
feat: add LNURL processor (#202)
* feat: add new lnurl processor * fix: lnbits issues * fix: add default settings for lnurl processor * fix: small changes * fix: more changes * fix: add verify url in upsert omit * fix: change comment * chore: add updateInvoiceStatus * chore: revert lnbits change * fix: changes
1 parent d803428 commit f237400

18 files changed

+159
-36
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
exports.up = function (knex) {
2+
return knex.raw('ALTER TABLE invoices ADD verify_url TEXT;')
3+
}
4+
5+
exports.down = function (knex) {
6+
return knex.schema.alterTable('invoices', function (table) {
7+
table.dropColumn('verify_url')
8+
})
9+
}

resources/default-settings.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ paymentsProcessors:
2828
lnbits:
2929
baseURL: https://lnbits.your-domain.com/
3030
callbackBaseURL: https://nostream.your-domain.com/callbacks/lnbits
31+
lnurl:
32+
invoiceURL: https://getalby.com/lnurlp/your-username
3133
network:
3234
maxPayloadSize: 524288
3335
# Comment the next line if using CloudFlare proxy

resources/invoices.html

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ <h2 class="text-danger">Invoice expired!</h2>
168168
if (event.pubkey === relayPubkey) {
169169
paid = true
170170

171-
clearTimeout(timeout)
171+
if (expiresAt) clearTimeout(timeout)
172172

173173
hide('pending')
174174
show('paid')
@@ -213,12 +213,14 @@ <h2 class="text-danger">Invoice expired!</h2>
213213
}
214214
}
215215

216-
const expiry = (new Date(expiresAt).getTime() - new Date().getTime())
217-
console.log('expiry at', expiresAt, Math.floor(expiry / 1000))
218-
timeout = setTimeout(() => {
219-
hide('pending')
220-
show('expired')
221-
}, expiry)
216+
if (expiresAt) {
217+
const expiry = (new Date(expiresAt).getTime() - new Date().getTime())
218+
console.log('expiry at', expiresAt, Math.floor(expiry / 1000))
219+
timeout = setTimeout(() => {
220+
hide('pending')
221+
show('expired')
222+
}, expiry)
223+
}
222224

223225
new QRCode(document.getElementById("invoice"), {
224226
text: `lightning:${invoice}`,

src/@types/clients.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface CreateInvoiceResponse {
1212
confirmedAt?: Date | null
1313
createdAt: Date
1414
rawResponse?: string
15+
verifyURL?: string
1516
}
1617

1718
export interface CreateInvoiceRequest {
@@ -20,9 +21,9 @@ export interface CreateInvoiceRequest {
2021
requestId?: string
2122
}
2223

23-
export type GetInvoiceResponse = Invoice
24+
export type GetInvoiceResponse = Partial<Invoice>
2425

2526
export interface IPaymentsProcessor {
2627
createInvoice(request: CreateInvoiceRequest): Promise<CreateInvoiceResponse>
27-
getInvoice(invoiceId: string): Promise<GetInvoiceResponse>
28+
getInvoice(invoice: string | Invoice): Promise<GetInvoiceResponse>
2829
}

src/@types/invoice.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface Invoice {
2424
expiresAt: Date | null
2525
updatedAt: Date
2626
createdAt: Date
27+
verifyURL?: string
2728
}
2829

2930
export interface DBInvoice {
@@ -39,4 +40,5 @@ export interface DBInvoice {
3940
expires_at: Date
4041
updated_at: Date
4142
created_at: Date
43+
verify_url: string
4244
}

src/@types/services.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { Invoice } from './invoice'
22
import { Pubkey } from './base'
33

44
export interface IPaymentsService {
5-
getInvoiceFromPaymentsProcessor(invoiceId: string): Promise<Invoice>
5+
getInvoiceFromPaymentsProcessor(invoice: string | Invoice): Promise<Partial<Invoice>>
66
createInvoice(
77
pubkey: Pubkey,
88
amount: bigint,
99
description: string,
1010
): Promise<Invoice>
11-
updateInvoice(invoice: Invoice): Promise<void>
11+
updateInvoice(invoice: Partial<Invoice>): Promise<void>
12+
updateInvoiceStatus(invoice: Partial<Invoice>): Promise<void>
1213
confirmInvoice(
1314
invoice: Pick<Invoice, 'id' | 'amountPaid' | 'confirmedAt'>,
1415
): Promise<void>

src/@types/settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ export interface Payments {
142142
feeSchedules: FeeSchedules
143143
}
144144

145+
export interface LnurlPaymentsProcessor {
146+
invoiceURL: string
147+
}
148+
145149
export interface ZebedeePaymentsProcessor {
146150
baseURL: string
147151
callbackBaseURL: string
@@ -154,6 +158,7 @@ export interface LNbitsPaymentProcessor {
154158
}
155159

156160
export interface PaymentsProcessors {
161+
lnurl?: LnurlPaymentsProcessor,
157162
zebedee?: ZebedeePaymentsProcessor
158163
lnbits?: LNbitsPaymentProcessor
159164
}

src/app/maintenance-worker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ export class MaintenanceWorker implements IRunnable {
4848
debug('invoice %s: %o', invoice.id, invoice)
4949
try {
5050
debug('getting invoice %s from payment processor', invoice.id)
51-
const updatedInvoice = await this.paymentsService.getInvoiceFromPaymentsProcessor(invoice.id)
51+
const updatedInvoice = await this.paymentsService.getInvoiceFromPaymentsProcessor(invoice)
5252
await delay()
53-
debug('updating invoice %s: %o', invoice.id, invoice)
54-
await this.paymentsService.updateInvoice(updatedInvoice)
53+
debug('updating invoice status %s: %o', invoice.id, invoice)
54+
await this.paymentsService.updateInvoiceStatus(updatedInvoice)
5555

5656
if (
5757
invoice.status !== updatedInvoice.status

src/constants/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export enum EventTags {
4040
}
4141

4242
export enum PaymentsProcessors {
43+
LNURL = 'lnurl',
4344
ZEBEDEE = 'zebedee',
4445
LNBITS = 'lnbits',
4546
}

src/controllers/callbacks/lnbits-callback-controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Request, Response } from 'express'
22

3+
import { Invoice, InvoiceStatus } from '../../@types/invoice'
34
import { createLogger } from '../../factories/logger-factory'
45
import { IController } from '../../@types/controllers'
56
import { IInvoiceRepository } from '../../@types/repositories'
6-
import { InvoiceStatus } from '../../@types/invoice'
77
import { IPaymentsService } from '../../@types/services'
88

99
const debug = createLogger('lnbits-callback-controller')
@@ -72,8 +72,8 @@ export class LNbitsCallbackController implements IController {
7272
invoice.amountPaid = invoice.amountRequested
7373

7474
try {
75-
await this.paymentsService.confirmInvoice(invoice)
76-
await this.paymentsService.sendInvoiceUpdateNotification(invoice)
75+
await this.paymentsService.confirmInvoice(invoice as Invoice)
76+
await this.paymentsService.sendInvoiceUpdateNotification(invoice as Invoice)
7777
} catch (error) {
7878
console.error(`Unable to confirm invoice ${invoice.id}`, error)
7979

0 commit comments

Comments
 (0)