From 6bd859fd43f44f4db0cd3336ca5f7771cd6e5d29 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Mon, 13 Feb 2023 18:56:11 +0530 Subject: [PATCH 01/10] feat: add new lnurl processor --- ...103904_add_verify_url_to_invoices_table.js | 9 +++ resources/invoices.html | 16 +++-- src/@types/clients.ts | 5 +- src/@types/invoice.ts | 2 + src/@types/services.ts | 4 +- src/@types/settings.ts | 5 ++ src/app/maintenance-worker.ts | 2 +- src/constants/base.ts | 1 + .../invoices/post-invoice-controller.ts | 2 +- src/factories/payments-processor-factory.ts | 16 +++++ .../lnbits-payment-processor.ts | 44 ++++++------ .../lnurl-payments-processor.ts | 69 +++++++++++++++++++ .../null-payments-processor.ts | 8 ++- src/payments-processors/payments-procesor.ts | 6 +- .../zebedee-payments-processor.ts | 9 +-- src/repositories/invoice-repository.ts | 5 ++ src/services/payments-service.ts | 18 ++--- src/utils/transform.ts | 1 + 18 files changed, 166 insertions(+), 56 deletions(-) create mode 100644 migrations/20230213103904_add_verify_url_to_invoices_table.js create mode 100644 src/payments-processors/lnurl-payments-processor.ts diff --git a/migrations/20230213103904_add_verify_url_to_invoices_table.js b/migrations/20230213103904_add_verify_url_to_invoices_table.js new file mode 100644 index 00000000..7ed03bf7 --- /dev/null +++ b/migrations/20230213103904_add_verify_url_to_invoices_table.js @@ -0,0 +1,9 @@ +exports.up = function (knex) { + return knex.raw('ALTER TABLE invoices ADD verify_url TEXT;') +} + +exports.down = function (knex) { + return knex.schema.alterTable('invoices', function (table) { + table.dropColumn('verify_url') + }) +} diff --git a/resources/invoices.html b/resources/invoices.html index 71d1c003..48ceca3c 100644 --- a/resources/invoices.html +++ b/resources/invoices.html @@ -168,7 +168,7 @@

Invoice expired!

if (event.pubkey === relayPubkey) { paid = true - clearTimeout(timeout) + if (expiresAt) clearTimeout(timeout) hide('pending') show('paid') @@ -213,12 +213,14 @@

Invoice expired!

} } - const expiry = (new Date(expiresAt).getTime() - new Date().getTime()) - console.log('expiry at', expiresAt, Math.floor(expiry / 1000)) - timeout = setTimeout(() => { - hide('pending') - show('expired') - }, expiry) + if (expiresAt) { + const expiry = (new Date(expiresAt).getTime() - new Date().getTime()) + console.log('expiry at', expiresAt, Math.floor(expiry / 1000)) + timeout = setTimeout(() => { + hide('pending') + show('expired') + }, expiry) + } new QRCode(document.getElementById("invoice"), { text: `lightning:${invoice}`, diff --git a/src/@types/clients.ts b/src/@types/clients.ts index 00fbc1a1..255ca80f 100644 --- a/src/@types/clients.ts +++ b/src/@types/clients.ts @@ -12,6 +12,7 @@ export interface CreateInvoiceResponse { confirmedAt?: Date | null createdAt: Date rawResponse?: string + verifyURL?: string } export interface CreateInvoiceRequest { @@ -20,9 +21,9 @@ export interface CreateInvoiceRequest { requestId?: string } -export type GetInvoiceResponse = Invoice +export type GetInvoiceResponse = Partial export interface IPaymentsProcessor { createInvoice(request: CreateInvoiceRequest): Promise - getInvoice(invoiceId: string): Promise + getInvoice(invoice: Invoice): Promise } diff --git a/src/@types/invoice.ts b/src/@types/invoice.ts index dbf9ec69..14b472bb 100644 --- a/src/@types/invoice.ts +++ b/src/@types/invoice.ts @@ -24,6 +24,7 @@ export interface Invoice { expiresAt: Date | null updatedAt: Date createdAt: Date + verifyURL?: string } export interface DBInvoice { @@ -39,4 +40,5 @@ export interface DBInvoice { expires_at: Date updated_at: Date created_at: Date + verify_url: string } diff --git a/src/@types/services.ts b/src/@types/services.ts index 4d3fb5d2..778dd1a5 100644 --- a/src/@types/services.ts +++ b/src/@types/services.ts @@ -2,13 +2,13 @@ import { Invoice } from './invoice' import { Pubkey } from './base' export interface IPaymentsService { - getInvoiceFromPaymentsProcessor(invoiceId: string): Promise + getInvoiceFromPaymentsProcessor(invoice: Invoice): Promise> createInvoice( pubkey: Pubkey, amount: bigint, description: string, ): Promise - updateInvoice(invoice: Invoice): Promise + updateInvoice(invoice: Partial): Promise confirmInvoice( invoice: Pick, ): Promise diff --git a/src/@types/settings.ts b/src/@types/settings.ts index da59af30..a0ddec7c 100644 --- a/src/@types/settings.ts +++ b/src/@types/settings.ts @@ -142,6 +142,10 @@ export interface Payments { feeSchedules: FeeSchedules } +export interface LnurlPaymentsProcessor { + invoiceURL: string +} + export interface ZebedeePaymentsProcessor { baseURL: string callbackBaseURL: string @@ -154,6 +158,7 @@ export interface LNbitsPaymentProcessor { } export interface PaymentsProcessors { + lnurl?: LnurlPaymentsProcessor, zebedee?: ZebedeePaymentsProcessor lnbits?: LNbitsPaymentProcessor } diff --git a/src/app/maintenance-worker.ts b/src/app/maintenance-worker.ts index bc7ddc50..3f60127e 100644 --- a/src/app/maintenance-worker.ts +++ b/src/app/maintenance-worker.ts @@ -48,7 +48,7 @@ export class MaintenanceWorker implements IRunnable { debug('invoice %s: %o', invoice.id, invoice) try { debug('getting invoice %s from payment processor', invoice.id) - const updatedInvoice = await this.paymentsService.getInvoiceFromPaymentsProcessor(invoice.id) + const updatedInvoice = await this.paymentsService.getInvoiceFromPaymentsProcessor(invoice) await delay() debug('updating invoice %s: %o', invoice.id, invoice) await this.paymentsService.updateInvoice(updatedInvoice) diff --git a/src/constants/base.ts b/src/constants/base.ts index 569da881..e23f4bb6 100644 --- a/src/constants/base.ts +++ b/src/constants/base.ts @@ -40,6 +40,7 @@ export enum EventTags { } export enum PaymentsProcessors { + LNURL = 'lnurl', ZEBEDEE = 'zebedee', LNBITS = 'lnbits', } diff --git a/src/controllers/invoices/post-invoice-controller.ts b/src/controllers/invoices/post-invoice-controller.ts index b4817e59..a3306576 100644 --- a/src/controllers/invoices/post-invoice-controller.ts +++ b/src/controllers/invoices/post-invoice-controller.ts @@ -165,7 +165,7 @@ export class PostInvoiceController implements IController { relay_url: relayUrl, pubkey, relay_pubkey: relayPubkey, - expires_at: invoice.expiresAt?.toISOString(), + expires_at: invoice.expiresAt?.toISOString() || '', invoice: invoice.bolt11, amount: amount / 1000n, } diff --git a/src/factories/payments-processor-factory.ts b/src/factories/payments-processor-factory.ts index e588eeac..a24c7500 100644 --- a/src/factories/payments-processor-factory.ts +++ b/src/factories/payments-processor-factory.ts @@ -5,6 +5,7 @@ import { createLogger } from './logger-factory' import { createSettings } from './settings-factory' import { IPaymentsProcessor } from '../@types/clients' import { LNbitsPaymentsProcesor } from '../payments-processors/lnbits-payment-processor' +import { LnurlPaymentsProcesor } from '../payments-processors/lnurl-payments-processor' import { NullPaymentsProcessor } from '../payments-processors/null-payments-processor' import { PaymentsProcessor } from '../payments-processors/payments-procesor' import { Settings } from '../@types/settings' @@ -44,6 +45,19 @@ const getLNbitsAxiosConfig = (settings: Settings): CreateAxiosDefaults => { } } +const createLnurlPaymentsProcessor = (settings: Settings): IPaymentsProcessor => { + const invoiceURL = path(['paymentsProcessors', 'lnurl', 'invoiceURL'], settings) as string | undefined + if (typeof invoiceURL === 'undefined') { + throw new Error('Unable to create payments processor: Setting paymentsProcessor.lnurl.invoiceURL is not configured.') + } + + const client = axios.create() + + const app = new LnurlPaymentsProcesor(client, createSettings) + + return new PaymentsProcessor(app) +} + const createZebedeePaymentsProcessor = (settings: Settings): IPaymentsProcessor => { const callbackBaseURL = path(['paymentsProcessors', 'zebedee', 'callbackBaseURL'], settings) as string | undefined if (typeof callbackBaseURL === 'undefined' || callbackBaseURL.indexOf('nostream.your-domain.com') >= 0) { @@ -98,6 +112,8 @@ export const createPaymentsProcessor = (): IPaymentsProcessor => { } switch (settings.payments?.processor) { + case 'lnurl': + return createLnurlPaymentsProcessor(settings) case 'zebedee': return createZebedeePaymentsProcessor(settings) case 'lnbits': diff --git a/src/payments-processors/lnbits-payment-processor.ts b/src/payments-processors/lnbits-payment-processor.ts index 6996c083..e394c58e 100644 --- a/src/payments-processors/lnbits-payment-processor.ts +++ b/src/payments-processors/lnbits-payment-processor.ts @@ -45,29 +45,29 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { private settings: Factory ) {} - public async getInvoice(invoiceId: string): Promise { - debug('get invoice: %s', invoiceId) + public async getInvoice(invoice: Invoice): Promise { + debug('get invoice: %s', invoice.id) try { - const response = await this.httpClient.get(`/api/v1/payments/${invoiceId}`, { + const response = await this.httpClient.get(`/api/v1/payments/${invoice.id}`, { maxRedirects: 1, }) - const invoice = new LNbitsInvoice() + const invoiceResult = new LNbitsInvoice() const data = response.data - invoice.id = data.details.payment_hash - invoice.pubkey = data.details.extra.internalId - invoice.bolt11 = data.details.bolt11 - invoice.amountRequested = BigInt(Math.floor(data.details.amount / 1000)) - if (data.paid) invoice.amountPaid = BigInt(Math.floor(data.details.amount / 1000)) - invoice.unit = InvoiceUnit.SATS - invoice.status = data.paid?InvoiceStatus.COMPLETED:InvoiceStatus.PENDING - invoice.description = data.details.memo - invoice.confirmedAt = data.paid ? new Date(data.details.time * 1000) : null - invoice.expiresAt = new Date(data.details.expiry * 1000) - invoice.createdAt = new Date(data.details.time * 1000) - invoice.updatedAt = new Date() - return invoice + invoiceResult.id = data.details.payment_hash + invoiceResult.pubkey = data.details.extra.internalId + invoiceResult.bolt11 = data.details.bolt11 + invoiceResult.amountRequested = BigInt(Math.floor(data.details.amount / 1000)) + if (data.paid) invoiceResult.amountPaid = BigInt(Math.floor(data.details.amount / 1000)) + invoiceResult.unit = InvoiceUnit.SATS + invoiceResult.status = data.paid?InvoiceStatus.COMPLETED:InvoiceStatus.PENDING + invoiceResult.description = data.details.memo + invoiceResult.confirmedAt = data.paid ? new Date(data.details.time * 1000) : null + invoiceResult.expiresAt = new Date(data.details.expiry * 1000) + invoiceResult.createdAt = new Date(data.details.time * 1000) + invoiceResult.updatedAt = new Date() + return invoiceResult } catch (error) { - console.error(`Unable to get invoice ${invoiceId}. Reason:`, error) + console.error(`Unable to get invoice ${invoice.id}. Reason:`, error) throw error } @@ -104,13 +104,13 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { debug('response: %o', response.data) - const invoiceResponse = await this.httpClient.get(`/api/v1/payments/${encodeURIComponent(response.data.payment_hash)}`, { + const invoiceResult = await this.httpClient.get(`/api/v1/payments/${encodeURIComponent(response.data.payment_hash)}`, { maxRedirects: 1, }) - debug('invoice data response: %o', invoiceResponse.data) + debug('invoice data response: %o', invoiceResult.data) const invoice = new LNbitsCreateInvoiceResponse() - const data = invoiceResponse.data + const data = invoiceResult.data invoice.id = data.details.payment_hash invoice.pubkey = data.details.extra.internalId invoice.bolt11 = data.details.bolt11 @@ -122,7 +122,7 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { invoice.expiresAt = new Date(data.details.expiry * 1000) invoice.createdAt = new Date(data.details.time * 1000) invoice.rawResponse = JSON.stringify({ - invoiceResponse: invoiceResponse.data, + invoiceResult: invoiceResult.data, createData: response.data, }) diff --git a/src/payments-processors/lnurl-payments-processor.ts b/src/payments-processors/lnurl-payments-processor.ts new file mode 100644 index 00000000..70b563a1 --- /dev/null +++ b/src/payments-processors/lnurl-payments-processor.ts @@ -0,0 +1,69 @@ +import { AxiosInstance } from 'axios' +import { Factory } from '../@types/base' + +import { CreateInvoiceRequest, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients' +import { Invoice, InvoiceStatus, InvoiceUnit } from '../@types/invoice' +import { createLogger } from '../factories/logger-factory' +import { randomUUID } from 'crypto' +import { Settings } from '../@types/settings' + +const debug = createLogger('alby-payments-processor') + +export class LnurlPaymentsProcesor implements IPaymentsProcessor { + public constructor( + private httpClient: AxiosInstance, + private settings: Factory + ) {} + + public async getInvoice(invoice: Invoice): Promise { + debug('get invoice: %s', invoice.id) + + try { + const response = await this.httpClient.get(invoice.verifyURL) + + return { + id: invoice.id, + status: response.data.settled ? InvoiceStatus['COMPLETED'] : InvoiceStatus['PENDING'], + } + } catch (error) { + console.error(`Unable to get invoice ${invoice.id}. Reason:`, error) + + throw error + } + } + + public async createInvoice(request: CreateInvoiceRequest): Promise { + debug('create invoice: %o', request) + const { + amount: amountMsats, + description, + requestId, + } = request + + try { + const response = await this.httpClient.get(`${this.settings().paymentsProcessors?.lnurl?.invoiceURL}/callback?amount=${amountMsats}&comment=${requestId}`) + + const result = { + id: randomUUID(), + pubkey: requestId, + bolt11: response.data.pr, + amountRequested: amountMsats, + description, + unit: InvoiceUnit['MSATS'], + status: InvoiceStatus['PENDING'], + expiresAt: null, + confirmedAt: null, + createdAt: new Date(), + verifyURL: response.data.verify, + } + + debug('result: %o', result) + + return result + } catch (error) { + console.error('Unable to request invoice. Reason:', error.message) + + throw error + } + } +} diff --git a/src/payments-processors/null-payments-processor.ts b/src/payments-processors/null-payments-processor.ts index 69f9f6df..5486fd7a 100644 --- a/src/payments-processors/null-payments-processor.ts +++ b/src/payments-processors/null-payments-processor.ts @@ -1,11 +1,11 @@ import { CreateInvoiceRequest, CreateInvoiceResponse, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients' -import { InvoiceStatus, InvoiceUnit } from '../@types/invoice' +import { Invoice, InvoiceStatus, InvoiceUnit } from '../@types/invoice' export class NullPaymentsProcessor implements IPaymentsProcessor { - public async getInvoice(invoiceId: string): Promise { + public async getInvoice(invoice: Invoice): Promise { const date = new Date() return { - id: invoiceId, + id: invoice.id, pubkey: '', bolt11: '', description: '', @@ -16,6 +16,7 @@ export class NullPaymentsProcessor implements IPaymentsProcessor { confirmedAt: null, createdAt: date, updatedAt: date, + verifyURL: '', } } @@ -32,6 +33,7 @@ export class NullPaymentsProcessor implements IPaymentsProcessor { rawResponse: '', confirmedAt: null, createdAt: new Date(), + verifyURL: '', } } } diff --git a/src/payments-processors/payments-procesor.ts b/src/payments-processors/payments-procesor.ts index ad64f489..648662cf 100644 --- a/src/payments-processors/payments-procesor.ts +++ b/src/payments-processors/payments-procesor.ts @@ -1,4 +1,4 @@ -import { CreateInvoiceRequest, CreateInvoiceResponse, IPaymentsProcessor } from '../@types/clients' +import { CreateInvoiceRequest, CreateInvoiceResponse, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients' import { Invoice } from '../@types/invoice' export class PaymentsProcessor implements IPaymentsProcessor { @@ -6,8 +6,8 @@ export class PaymentsProcessor implements IPaymentsProcessor { private readonly processor: IPaymentsProcessor ) {} - public async getInvoice(invoiceId: string): Promise { - return this.processor.getInvoice(invoiceId) + public async getInvoice(invoice: Invoice): Promise { + return this.processor.getInvoice(invoice) } public async createInvoice(request: CreateInvoiceRequest): Promise { diff --git a/src/payments-processors/zebedee-payments-processor.ts b/src/payments-processors/zebedee-payments-processor.ts index 1b8339db..d0bf134f 100644 --- a/src/payments-processors/zebedee-payments-processor.ts +++ b/src/payments-processors/zebedee-payments-processor.ts @@ -4,6 +4,7 @@ import { Factory } from '../@types/base' import { CreateInvoiceRequest, CreateInvoiceResponse, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients' import { createLogger } from '../factories/logger-factory' import { fromZebedeeInvoice } from '../utils/transform' +import { Invoice } from '../@types/invoice' import { Settings } from '../@types/settings' const debug = createLogger('zebedee-payments-processor') @@ -14,17 +15,17 @@ export class ZebedeePaymentsProcesor implements IPaymentsProcessor { private settings: Factory ) {} - public async getInvoice(invoiceId: string): Promise { - debug('get invoice: %s', invoiceId) + public async getInvoice(invoice: Invoice): Promise { + debug('get invoice: %s', invoice.id) try { - const response = await this.httpClient.get(`/v0/charges/${invoiceId}`, { + const response = await this.httpClient.get(`/v0/charges/${invoice.id}`, { maxRedirects: 1, }) return fromZebedeeInvoice(response.data.data) } catch (error) { - console.error(`Unable to get invoice ${invoiceId}. Reason:`, error) + console.error(`Unable to get invoice ${invoice.id}. Reason:`, error) throw error } diff --git a/src/repositories/invoice-repository.ts b/src/repositories/invoice-repository.ts index e493c425..9469a865 100644 --- a/src/repositories/invoice-repository.ts +++ b/src/repositories/invoice-repository.ts @@ -103,6 +103,11 @@ export class InvoiceRepository implements IInvoiceRepository { always(undefined), prop('createdAt'), ), + verify_url: ifElse( + propSatisfies(isNil, 'verifyURL'), + always(undefined), + prop('verifyURL'), + ), })(invoice) debug('row: %o', row) diff --git a/src/services/payments-service.ts b/src/services/payments-service.ts index e7c52273..83d674ea 100644 --- a/src/services/payments-service.ts +++ b/src/services/payments-service.ts @@ -36,10 +36,10 @@ export class PaymentsService implements IPaymentsService { } } - public async getInvoiceFromPaymentsProcessor(invoiceId: string): Promise { - debug('get invoice %s from payment processor', invoiceId) + public async getInvoiceFromPaymentsProcessor(invoice: Invoice): Promise> { + debug('get invoice %s from payment processor', invoice.id) try { - return await this.paymentsProcessor.getInvoice(invoiceId) + return await this.paymentsProcessor.getInvoice(invoice) } catch (error) { console.log('Unable to get invoice from payments processor. Reason:', error) @@ -82,6 +82,7 @@ export class PaymentsService implements IPaymentsService { expiresAt: invoiceResponse.expiresAt, updatedAt: date, createdAt: date, + verifyURL: invoiceResponse.verifyURL, }, transaction.transaction, ) @@ -99,6 +100,7 @@ export class PaymentsService implements IPaymentsService { expiresAt: invoiceResponse.expiresAt, updatedAt: date, createdAt: invoiceResponse.createdAt, + verifyURL: invoiceResponse.verifyURL, } } catch (error) { await transaction.rollback() @@ -111,17 +113,11 @@ export class PaymentsService implements IPaymentsService { public async updateInvoice(invoice: Invoice): Promise { debug('update invoice %s: %o', invoice.id, invoice) try { + const fullInvoice = await this.invoiceRepository.findById(invoice.id) await this.invoiceRepository.upsert({ - id: invoice.id, - pubkey: invoice.pubkey, - bolt11: invoice.bolt11, - amountRequested: invoice.amountRequested, - description: invoice.description, - unit: invoice.unit, + ...fullInvoice, status: invoice.status, - expiresAt: invoice.expiresAt, updatedAt: new Date(), - createdAt: invoice.createdAt, }) } catch (error) { console.error('Unable to update invoice. Reason:', error) diff --git a/src/utils/transform.ts b/src/utils/transform.ts index a5b8ccf9..81b04a69 100644 --- a/src/utils/transform.ts +++ b/src/utils/transform.ts @@ -31,6 +31,7 @@ export const fromDBInvoice = applySpec({ expiresAt: prop('expires_at'), updatedAt: prop('updated_at'), createdAt: prop('created_at'), + verifyURL: prop('verify_url'), }) export const fromDBUser = applySpec({ From 07b7c1bf6d74e35dd2e98069e004b107c418c82a Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 24 Feb 2023 21:34:26 +0530 Subject: [PATCH 02/10] fix: lnbits issues --- src/controllers/callbacks/lnbits-callback-controller.ts | 6 +++--- src/payments-processors/lnbits-payment-processor.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/controllers/callbacks/lnbits-callback-controller.ts b/src/controllers/callbacks/lnbits-callback-controller.ts index a4e3a480..54fc141c 100644 --- a/src/controllers/callbacks/lnbits-callback-controller.ts +++ b/src/controllers/callbacks/lnbits-callback-controller.ts @@ -1,9 +1,9 @@ import { Request, Response } from 'express' +import { Invoice, InvoiceStatus } from '../../@types/invoice' import { createLogger } from '../../factories/logger-factory' import { IController } from '../../@types/controllers' import { IInvoiceRepository } from '../../@types/repositories' -import { InvoiceStatus } from '../../@types/invoice' import { IPaymentsService } from '../../@types/services' const debug = createLogger('lnbits-callback-controller') @@ -72,8 +72,8 @@ export class LNbitsCallbackController implements IController { invoice.amountPaid = invoice.amountRequested try { - await this.paymentsService.confirmInvoice(invoice) - await this.paymentsService.sendInvoiceUpdateNotification(invoice) + await this.paymentsService.confirmInvoice(invoice as Invoice) + await this.paymentsService.sendInvoiceUpdateNotification(invoice as Invoice) } catch (error) { console.error(`Unable to confirm invoice ${invoice.id}`, error) diff --git a/src/payments-processors/lnbits-payment-processor.ts b/src/payments-processors/lnbits-payment-processor.ts index e394c58e..05d4ba50 100644 --- a/src/payments-processors/lnbits-payment-processor.ts +++ b/src/payments-processors/lnbits-payment-processor.ts @@ -104,13 +104,13 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { debug('response: %o', response.data) - const invoiceResult = await this.httpClient.get(`/api/v1/payments/${encodeURIComponent(response.data.payment_hash)}`, { + const invoiceResponse = await this.httpClient.get(`/api/v1/payments/${encodeURIComponent(response.data.payment_hash)}`, { maxRedirects: 1, }) - debug('invoice data response: %o', invoiceResult.data) + debug('invoice data response: %o', invoiceResponse.data) const invoice = new LNbitsCreateInvoiceResponse() - const data = invoiceResult.data + const data = invoiceResponse.data invoice.id = data.details.payment_hash invoice.pubkey = data.details.extra.internalId invoice.bolt11 = data.details.bolt11 @@ -122,7 +122,7 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { invoice.expiresAt = new Date(data.details.expiry * 1000) invoice.createdAt = new Date(data.details.time * 1000) invoice.rawResponse = JSON.stringify({ - invoiceResult: invoiceResult.data, + invoiceResult: invoiceResponse.data, createData: response.data, }) From add2f5dce7daab40c35c365016401a1562188e1d Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 24 Feb 2023 21:37:44 +0530 Subject: [PATCH 03/10] fix: add default settings for lnurl processor --- resources/default-settings.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/default-settings.yaml b/resources/default-settings.yaml index 4f76cecf..a27ddd32 100755 --- a/resources/default-settings.yaml +++ b/resources/default-settings.yaml @@ -28,6 +28,8 @@ paymentsProcessors: lnbits: baseURL: https://lnbits.your-domain.com/ callbackBaseURL: https://nostream.your-domain.com/callbacks/lnbits + lnurl: + invoiceURL: https://getalby.com/lnurlp/your-username network: maxPayloadSize: 524288 # Comment the next line if using CloudFlare proxy From 4fe0679c78462d43e97d40ccad161c6c868bab25 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 24 Feb 2023 21:40:12 +0530 Subject: [PATCH 04/10] fix: small changes --- src/controllers/invoices/post-invoice-controller.ts | 2 +- src/payments-processors/lnurl-payments-processor.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/invoices/post-invoice-controller.ts b/src/controllers/invoices/post-invoice-controller.ts index a3306576..646ba85e 100644 --- a/src/controllers/invoices/post-invoice-controller.ts +++ b/src/controllers/invoices/post-invoice-controller.ts @@ -165,7 +165,7 @@ export class PostInvoiceController implements IController { relay_url: relayUrl, pubkey, relay_pubkey: relayPubkey, - expires_at: invoice.expiresAt?.toISOString() || '', + expires_at: invoice.expiresAt?.toISOString() ?? '', invoice: invoice.bolt11, amount: amount / 1000n, } diff --git a/src/payments-processors/lnurl-payments-processor.ts b/src/payments-processors/lnurl-payments-processor.ts index 70b563a1..b132b255 100644 --- a/src/payments-processors/lnurl-payments-processor.ts +++ b/src/payments-processors/lnurl-payments-processor.ts @@ -7,7 +7,7 @@ import { createLogger } from '../factories/logger-factory' import { randomUUID } from 'crypto' import { Settings } from '../@types/settings' -const debug = createLogger('alby-payments-processor') +const debug = createLogger('lnurl-payments-processor') export class LnurlPaymentsProcesor implements IPaymentsProcessor { public constructor( From c3b3371e83c3580c9f589f22322923bb18d37d62 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 24 Feb 2023 22:03:38 +0530 Subject: [PATCH 05/10] fix: more changes --- .../lnurl-payments-processor.ts | 4 ++-- src/repositories/invoice-repository.ts | 19 +++---------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/payments-processors/lnurl-payments-processor.ts b/src/payments-processors/lnurl-payments-processor.ts index b132b255..2d049613 100644 --- a/src/payments-processors/lnurl-payments-processor.ts +++ b/src/payments-processors/lnurl-payments-processor.ts @@ -49,8 +49,8 @@ export class LnurlPaymentsProcesor implements IPaymentsProcessor { bolt11: response.data.pr, amountRequested: amountMsats, description, - unit: InvoiceUnit['MSATS'], - status: InvoiceStatus['PENDING'], + unit: InvoiceUnit.MSATS, + status: InvoiceStatus.PENDING, expiresAt: null, confirmedAt: null, createdAt: new Date(), diff --git a/src/repositories/invoice-repository.ts b/src/repositories/invoice-repository.ts index 9469a865..388e6b57 100644 --- a/src/repositories/invoice-repository.ts +++ b/src/repositories/invoice-repository.ts @@ -3,7 +3,6 @@ import { applySpec, ifElse, is, - isNil, omit, pipe, prop, @@ -92,22 +91,10 @@ export class InvoiceRepository implements IInvoiceRepository { status: prop('status'), description: prop('description'), // confirmed_at: prop('confirmedAt'), - expires_at: ifElse( - propSatisfies(isNil, 'expiresAt'), - always(undefined), - prop('expiresAt'), - ), + expires_at: prop('expiresAt'), updated_at: always(new Date()), - created_at: ifElse( - propSatisfies(isNil, 'createdAt'), - always(undefined), - prop('createdAt'), - ), - verify_url: ifElse( - propSatisfies(isNil, 'verifyURL'), - always(undefined), - prop('verifyURL'), - ), + created_at: prop('createdAt'), + verify_url: prop('verifyURL'), })(invoice) debug('row: %o', row) From 259ded1293506f2fe7adf7700ba58bc35b83e746 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 24 Feb 2023 22:09:10 +0530 Subject: [PATCH 06/10] fix: add verify url in upsert omit --- src/repositories/invoice-repository.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/repositories/invoice-repository.ts b/src/repositories/invoice-repository.ts index 388e6b57..381457a5 100644 --- a/src/repositories/invoice-repository.ts +++ b/src/repositories/invoice-repository.ts @@ -112,6 +112,7 @@ export class InvoiceRepository implements IInvoiceRepository { 'description', 'expires_at', 'created_at', + 'verify_url', ])(row) ) From 69fc0f55fbe0420dedf251a4d3eb1c77e482e0bf Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 24 Feb 2023 23:12:19 +0530 Subject: [PATCH 07/10] fix: change comment --- src/payments-processors/lnurl-payments-processor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/payments-processors/lnurl-payments-processor.ts b/src/payments-processors/lnurl-payments-processor.ts index 2d049613..7c0b275c 100644 --- a/src/payments-processors/lnurl-payments-processor.ts +++ b/src/payments-processors/lnurl-payments-processor.ts @@ -41,7 +41,7 @@ export class LnurlPaymentsProcesor implements IPaymentsProcessor { } = request try { - const response = await this.httpClient.get(`${this.settings().paymentsProcessors?.lnurl?.invoiceURL}/callback?amount=${amountMsats}&comment=${requestId}`) + const response = await this.httpClient.get(`${this.settings().paymentsProcessors?.lnurl?.invoiceURL}/callback?amount=${amountMsats}&comment=${description}`) const result = { id: randomUUID(), From 77086277858803a06b1fd55647cbd9d19a91c8ee Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 28 Feb 2023 00:00:03 +0530 Subject: [PATCH 08/10] chore: add updateInvoiceStatus --- src/@types/clients.ts | 2 +- src/@types/services.ts | 3 +- src/app/maintenance-worker.ts | 4 +-- .../lnbits-payment-processor.ts | 8 ++--- .../null-payments-processor.ts | 6 ++-- src/payments-processors/payments-procesor.ts | 2 +- .../zebedee-payments-processor.ts | 9 +++--- src/services/payments-service.ts | 31 ++++++++++++++++--- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/@types/clients.ts b/src/@types/clients.ts index 255ca80f..8f7ad151 100644 --- a/src/@types/clients.ts +++ b/src/@types/clients.ts @@ -25,5 +25,5 @@ export type GetInvoiceResponse = Partial export interface IPaymentsProcessor { createInvoice(request: CreateInvoiceRequest): Promise - getInvoice(invoice: Invoice): Promise + getInvoice(invoice: string | Invoice): Promise } diff --git a/src/@types/services.ts b/src/@types/services.ts index 778dd1a5..8058f5ae 100644 --- a/src/@types/services.ts +++ b/src/@types/services.ts @@ -2,13 +2,14 @@ import { Invoice } from './invoice' import { Pubkey } from './base' export interface IPaymentsService { - getInvoiceFromPaymentsProcessor(invoice: Invoice): Promise> + getInvoiceFromPaymentsProcessor(invoice: string | Invoice): Promise> createInvoice( pubkey: Pubkey, amount: bigint, description: string, ): Promise updateInvoice(invoice: Partial): Promise + updateInvoiceStatus(invoice: Partial): Promise confirmInvoice( invoice: Pick, ): Promise diff --git a/src/app/maintenance-worker.ts b/src/app/maintenance-worker.ts index 3f60127e..4570a072 100644 --- a/src/app/maintenance-worker.ts +++ b/src/app/maintenance-worker.ts @@ -50,8 +50,8 @@ export class MaintenanceWorker implements IRunnable { debug('getting invoice %s from payment processor', invoice.id) const updatedInvoice = await this.paymentsService.getInvoiceFromPaymentsProcessor(invoice) await delay() - debug('updating invoice %s: %o', invoice.id, invoice) - await this.paymentsService.updateInvoice(updatedInvoice) + debug('updating invoice status %s: %o', invoice.id, invoice) + await this.paymentsService.updateInvoiceStatus(updatedInvoice) if ( invoice.status !== updatedInvoice.status diff --git a/src/payments-processors/lnbits-payment-processor.ts b/src/payments-processors/lnbits-payment-processor.ts index 05d4ba50..02b77529 100644 --- a/src/payments-processors/lnbits-payment-processor.ts +++ b/src/payments-processors/lnbits-payment-processor.ts @@ -45,10 +45,10 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { private settings: Factory ) {} - public async getInvoice(invoice: Invoice): Promise { - debug('get invoice: %s', invoice.id) + public async getInvoice(invoiceId: string): Promise { + debug('get invoice: %s', invoiceId) try { - const response = await this.httpClient.get(`/api/v1/payments/${invoice.id}`, { + const response = await this.httpClient.get(`/api/v1/payments/${invoiceId}`, { maxRedirects: 1, }) const invoiceResult = new LNbitsInvoice() @@ -67,7 +67,7 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { invoiceResult.updatedAt = new Date() return invoiceResult } catch (error) { - console.error(`Unable to get invoice ${invoice.id}. Reason:`, error) + console.error(`Unable to get invoice ${invoiceId}. Reason:`, error) throw error } diff --git a/src/payments-processors/null-payments-processor.ts b/src/payments-processors/null-payments-processor.ts index 5486fd7a..9e5b4a9f 100644 --- a/src/payments-processors/null-payments-processor.ts +++ b/src/payments-processors/null-payments-processor.ts @@ -1,11 +1,11 @@ import { CreateInvoiceRequest, CreateInvoiceResponse, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients' -import { Invoice, InvoiceStatus, InvoiceUnit } from '../@types/invoice' +import { InvoiceStatus, InvoiceUnit } from '../@types/invoice' export class NullPaymentsProcessor implements IPaymentsProcessor { - public async getInvoice(invoice: Invoice): Promise { + public async getInvoice(invoiceId: string): Promise { const date = new Date() return { - id: invoice.id, + id: invoiceId, pubkey: '', bolt11: '', description: '', diff --git a/src/payments-processors/payments-procesor.ts b/src/payments-processors/payments-procesor.ts index 648662cf..22c41ef3 100644 --- a/src/payments-processors/payments-procesor.ts +++ b/src/payments-processors/payments-procesor.ts @@ -6,7 +6,7 @@ export class PaymentsProcessor implements IPaymentsProcessor { private readonly processor: IPaymentsProcessor ) {} - public async getInvoice(invoice: Invoice): Promise { + public async getInvoice(invoice: string | Invoice): Promise { return this.processor.getInvoice(invoice) } diff --git a/src/payments-processors/zebedee-payments-processor.ts b/src/payments-processors/zebedee-payments-processor.ts index d0bf134f..1b8339db 100644 --- a/src/payments-processors/zebedee-payments-processor.ts +++ b/src/payments-processors/zebedee-payments-processor.ts @@ -4,7 +4,6 @@ import { Factory } from '../@types/base' import { CreateInvoiceRequest, CreateInvoiceResponse, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients' import { createLogger } from '../factories/logger-factory' import { fromZebedeeInvoice } from '../utils/transform' -import { Invoice } from '../@types/invoice' import { Settings } from '../@types/settings' const debug = createLogger('zebedee-payments-processor') @@ -15,17 +14,17 @@ export class ZebedeePaymentsProcesor implements IPaymentsProcessor { private settings: Factory ) {} - public async getInvoice(invoice: Invoice): Promise { - debug('get invoice: %s', invoice.id) + public async getInvoice(invoiceId: string): Promise { + debug('get invoice: %s', invoiceId) try { - const response = await this.httpClient.get(`/v0/charges/${invoice.id}`, { + const response = await this.httpClient.get(`/v0/charges/${invoiceId}`, { maxRedirects: 1, }) return fromZebedeeInvoice(response.data.data) } catch (error) { - console.error(`Unable to get invoice ${invoice.id}. Reason:`, error) + console.error(`Unable to get invoice ${invoiceId}. Reason:`, error) throw error } diff --git a/src/services/payments-service.ts b/src/services/payments-service.ts index 83d674ea..7c43c904 100644 --- a/src/services/payments-service.ts +++ b/src/services/payments-service.ts @@ -39,7 +39,9 @@ export class PaymentsService implements IPaymentsService { public async getInvoiceFromPaymentsProcessor(invoice: Invoice): Promise> { debug('get invoice %s from payment processor', invoice.id) try { - return await this.paymentsProcessor.getInvoice(invoice) + return await this.paymentsProcessor.getInvoice( + this.settings().payments?.processor === 'lnurl' ? invoice : invoice.id + ) } catch (error) { console.log('Unable to get invoice from payments processor. Reason:', error) @@ -110,7 +112,28 @@ export class PaymentsService implements IPaymentsService { } } - public async updateInvoice(invoice: Invoice): Promise { + public async updateInvoice(invoice: Partial): Promise { + debug('update invoice %s: %o', invoice.id, invoice) + try { + await this.invoiceRepository.upsert({ + id: invoice.id, + pubkey: invoice.pubkey, + bolt11: invoice.bolt11, + amountRequested: invoice.amountRequested, + description: invoice.description, + unit: invoice.unit, + status: invoice.status, + expiresAt: invoice.expiresAt, + updatedAt: new Date(), + createdAt: invoice.createdAt, + }) + } catch (error) { + console.error('Unable to update invoice. Reason:', error) + throw error + } + } + + public async updateInvoiceStatus(invoice: Partial): Promise { debug('update invoice %s: %o', invoice.id, invoice) try { const fullInvoice = await this.invoiceRepository.findById(invoice.id) @@ -208,7 +231,7 @@ export class PaymentsService implements IPaymentsService { }, } = currentSettings - const relayPrivkey = getRelayPrivateKey(relayUrl) + const relayPrivkey = 'b7eab8ab34aac491217a31059ec017e51c63d09c828e39ee3a40a016bc9d0cbf' || getRelayPrivateKey(relayUrl) const relayPubkey = getPublicKey(relayPrivkey) let unit: string = invoice.unit @@ -270,7 +293,7 @@ ${invoice.bolt11}`, }, } = currentSettings - const relayPrivkey = getRelayPrivateKey(relayUrl) + const relayPrivkey = 'nsec1y9dk6e95kx54e076kfyt3km2ymp39r2l4zww64y9xc0gr3duyprqedf7kh' //getRelayPrivateKey(relayUrl) const relayPubkey = getPublicKey(relayPrivkey) let unit: string = invoice.unit From ce96b7b6a4630a898e3581ba8cbd30b8f289f113 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 28 Feb 2023 00:02:55 +0530 Subject: [PATCH 09/10] chore: revert lnbits change --- .../lnbits-payment-processor.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/payments-processors/lnbits-payment-processor.ts b/src/payments-processors/lnbits-payment-processor.ts index 02b77529..6996c083 100644 --- a/src/payments-processors/lnbits-payment-processor.ts +++ b/src/payments-processors/lnbits-payment-processor.ts @@ -51,21 +51,21 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { const response = await this.httpClient.get(`/api/v1/payments/${invoiceId}`, { maxRedirects: 1, }) - const invoiceResult = new LNbitsInvoice() + const invoice = new LNbitsInvoice() const data = response.data - invoiceResult.id = data.details.payment_hash - invoiceResult.pubkey = data.details.extra.internalId - invoiceResult.bolt11 = data.details.bolt11 - invoiceResult.amountRequested = BigInt(Math.floor(data.details.amount / 1000)) - if (data.paid) invoiceResult.amountPaid = BigInt(Math.floor(data.details.amount / 1000)) - invoiceResult.unit = InvoiceUnit.SATS - invoiceResult.status = data.paid?InvoiceStatus.COMPLETED:InvoiceStatus.PENDING - invoiceResult.description = data.details.memo - invoiceResult.confirmedAt = data.paid ? new Date(data.details.time * 1000) : null - invoiceResult.expiresAt = new Date(data.details.expiry * 1000) - invoiceResult.createdAt = new Date(data.details.time * 1000) - invoiceResult.updatedAt = new Date() - return invoiceResult + invoice.id = data.details.payment_hash + invoice.pubkey = data.details.extra.internalId + invoice.bolt11 = data.details.bolt11 + invoice.amountRequested = BigInt(Math.floor(data.details.amount / 1000)) + if (data.paid) invoice.amountPaid = BigInt(Math.floor(data.details.amount / 1000)) + invoice.unit = InvoiceUnit.SATS + invoice.status = data.paid?InvoiceStatus.COMPLETED:InvoiceStatus.PENDING + invoice.description = data.details.memo + invoice.confirmedAt = data.paid ? new Date(data.details.time * 1000) : null + invoice.expiresAt = new Date(data.details.expiry * 1000) + invoice.createdAt = new Date(data.details.time * 1000) + invoice.updatedAt = new Date() + return invoice } catch (error) { console.error(`Unable to get invoice ${invoiceId}. Reason:`, error) @@ -122,7 +122,7 @@ export class LNbitsPaymentsProcesor implements IPaymentsProcessor { invoice.expiresAt = new Date(data.details.expiry * 1000) invoice.createdAt = new Date(data.details.time * 1000) invoice.rawResponse = JSON.stringify({ - invoiceResult: invoiceResponse.data, + invoiceResponse: invoiceResponse.data, createData: response.data, }) From 2943b3f22ee2d6a94aa34c8b867373c0a8a63c56 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 28 Feb 2023 00:05:19 +0530 Subject: [PATCH 10/10] fix: changes --- src/services/payments-service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/payments-service.ts b/src/services/payments-service.ts index 7c43c904..7e03e067 100644 --- a/src/services/payments-service.ts +++ b/src/services/payments-service.ts @@ -231,7 +231,7 @@ export class PaymentsService implements IPaymentsService { }, } = currentSettings - const relayPrivkey = 'b7eab8ab34aac491217a31059ec017e51c63d09c828e39ee3a40a016bc9d0cbf' || getRelayPrivateKey(relayUrl) + const relayPrivkey = getRelayPrivateKey(relayUrl) const relayPubkey = getPublicKey(relayPrivkey) let unit: string = invoice.unit @@ -293,7 +293,7 @@ ${invoice.bolt11}`, }, } = currentSettings - const relayPrivkey = 'nsec1y9dk6e95kx54e076kfyt3km2ymp39r2l4zww64y9xc0gr3duyprqedf7kh' //getRelayPrivateKey(relayUrl) + const relayPrivkey = getRelayPrivateKey(relayUrl) const relayPubkey = getPublicKey(relayPrivkey) let unit: string = invoice.unit