mirror of
https://github.com/Cameri/nostream.git
synced 2025-09-17 19:13:35 +02:00
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
This commit is contained in:
@@ -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')
|
||||||
|
})
|
||||||
|
}
|
@@ -28,6 +28,8 @@ paymentsProcessors:
|
|||||||
lnbits:
|
lnbits:
|
||||||
baseURL: https://lnbits.your-domain.com/
|
baseURL: https://lnbits.your-domain.com/
|
||||||
callbackBaseURL: https://nostream.your-domain.com/callbacks/lnbits
|
callbackBaseURL: https://nostream.your-domain.com/callbacks/lnbits
|
||||||
|
lnurl:
|
||||||
|
invoiceURL: https://getalby.com/lnurlp/your-username
|
||||||
network:
|
network:
|
||||||
maxPayloadSize: 524288
|
maxPayloadSize: 524288
|
||||||
# Comment the next line if using CloudFlare proxy
|
# Comment the next line if using CloudFlare proxy
|
||||||
|
@@ -168,7 +168,7 @@
|
|||||||
if (event.pubkey === relayPubkey) {
|
if (event.pubkey === relayPubkey) {
|
||||||
paid = true
|
paid = true
|
||||||
|
|
||||||
clearTimeout(timeout)
|
if (expiresAt) clearTimeout(timeout)
|
||||||
|
|
||||||
hide('pending')
|
hide('pending')
|
||||||
show('paid')
|
show('paid')
|
||||||
@@ -213,12 +213,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (expiresAt) {
|
||||||
const expiry = (new Date(expiresAt).getTime() - new Date().getTime())
|
const expiry = (new Date(expiresAt).getTime() - new Date().getTime())
|
||||||
console.log('expiry at', expiresAt, Math.floor(expiry / 1000))
|
console.log('expiry at', expiresAt, Math.floor(expiry / 1000))
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
hide('pending')
|
hide('pending')
|
||||||
show('expired')
|
show('expired')
|
||||||
}, expiry)
|
}, expiry)
|
||||||
|
}
|
||||||
|
|
||||||
new QRCode(document.getElementById("invoice"), {
|
new QRCode(document.getElementById("invoice"), {
|
||||||
text: `lightning:${invoice}`,
|
text: `lightning:${invoice}`,
|
||||||
|
@@ -12,6 +12,7 @@ export interface CreateInvoiceResponse {
|
|||||||
confirmedAt?: Date | null
|
confirmedAt?: Date | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
rawResponse?: string
|
rawResponse?: string
|
||||||
|
verifyURL?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateInvoiceRequest {
|
export interface CreateInvoiceRequest {
|
||||||
@@ -20,9 +21,9 @@ export interface CreateInvoiceRequest {
|
|||||||
requestId?: string
|
requestId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetInvoiceResponse = Invoice
|
export type GetInvoiceResponse = Partial<Invoice>
|
||||||
|
|
||||||
export interface IPaymentsProcessor {
|
export interface IPaymentsProcessor {
|
||||||
createInvoice(request: CreateInvoiceRequest): Promise<CreateInvoiceResponse>
|
createInvoice(request: CreateInvoiceRequest): Promise<CreateInvoiceResponse>
|
||||||
getInvoice(invoiceId: string): Promise<GetInvoiceResponse>
|
getInvoice(invoice: string | Invoice): Promise<GetInvoiceResponse>
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ export interface Invoice {
|
|||||||
expiresAt: Date | null
|
expiresAt: Date | null
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
verifyURL?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DBInvoice {
|
export interface DBInvoice {
|
||||||
@@ -39,4 +40,5 @@ export interface DBInvoice {
|
|||||||
expires_at: Date
|
expires_at: Date
|
||||||
updated_at: Date
|
updated_at: Date
|
||||||
created_at: Date
|
created_at: Date
|
||||||
|
verify_url: string
|
||||||
}
|
}
|
||||||
|
@@ -2,13 +2,14 @@ import { Invoice } from './invoice'
|
|||||||
import { Pubkey } from './base'
|
import { Pubkey } from './base'
|
||||||
|
|
||||||
export interface IPaymentsService {
|
export interface IPaymentsService {
|
||||||
getInvoiceFromPaymentsProcessor(invoiceId: string): Promise<Invoice>
|
getInvoiceFromPaymentsProcessor(invoice: string | Invoice): Promise<Partial<Invoice>>
|
||||||
createInvoice(
|
createInvoice(
|
||||||
pubkey: Pubkey,
|
pubkey: Pubkey,
|
||||||
amount: bigint,
|
amount: bigint,
|
||||||
description: string,
|
description: string,
|
||||||
): Promise<Invoice>
|
): Promise<Invoice>
|
||||||
updateInvoice(invoice: Invoice): Promise<void>
|
updateInvoice(invoice: Partial<Invoice>): Promise<void>
|
||||||
|
updateInvoiceStatus(invoice: Partial<Invoice>): Promise<void>
|
||||||
confirmInvoice(
|
confirmInvoice(
|
||||||
invoice: Pick<Invoice, 'id' | 'amountPaid' | 'confirmedAt'>,
|
invoice: Pick<Invoice, 'id' | 'amountPaid' | 'confirmedAt'>,
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
|
@@ -142,6 +142,10 @@ export interface Payments {
|
|||||||
feeSchedules: FeeSchedules
|
feeSchedules: FeeSchedules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LnurlPaymentsProcessor {
|
||||||
|
invoiceURL: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface ZebedeePaymentsProcessor {
|
export interface ZebedeePaymentsProcessor {
|
||||||
baseURL: string
|
baseURL: string
|
||||||
callbackBaseURL: string
|
callbackBaseURL: string
|
||||||
@@ -154,6 +158,7 @@ export interface LNbitsPaymentProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PaymentsProcessors {
|
export interface PaymentsProcessors {
|
||||||
|
lnurl?: LnurlPaymentsProcessor,
|
||||||
zebedee?: ZebedeePaymentsProcessor
|
zebedee?: ZebedeePaymentsProcessor
|
||||||
lnbits?: LNbitsPaymentProcessor
|
lnbits?: LNbitsPaymentProcessor
|
||||||
}
|
}
|
||||||
|
@@ -48,10 +48,10 @@ export class MaintenanceWorker implements IRunnable {
|
|||||||
debug('invoice %s: %o', invoice.id, invoice)
|
debug('invoice %s: %o', invoice.id, invoice)
|
||||||
try {
|
try {
|
||||||
debug('getting invoice %s from payment processor', invoice.id)
|
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()
|
await delay()
|
||||||
debug('updating invoice %s: %o', invoice.id, invoice)
|
debug('updating invoice status %s: %o', invoice.id, invoice)
|
||||||
await this.paymentsService.updateInvoice(updatedInvoice)
|
await this.paymentsService.updateInvoiceStatus(updatedInvoice)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
invoice.status !== updatedInvoice.status
|
invoice.status !== updatedInvoice.status
|
||||||
|
@@ -40,6 +40,7 @@ export enum EventTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum PaymentsProcessors {
|
export enum PaymentsProcessors {
|
||||||
|
LNURL = 'lnurl',
|
||||||
ZEBEDEE = 'zebedee',
|
ZEBEDEE = 'zebedee',
|
||||||
LNBITS = 'lnbits',
|
LNBITS = 'lnbits',
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
|
|
||||||
|
import { Invoice, InvoiceStatus } from '../../@types/invoice'
|
||||||
import { createLogger } from '../../factories/logger-factory'
|
import { createLogger } from '../../factories/logger-factory'
|
||||||
import { IController } from '../../@types/controllers'
|
import { IController } from '../../@types/controllers'
|
||||||
import { IInvoiceRepository } from '../../@types/repositories'
|
import { IInvoiceRepository } from '../../@types/repositories'
|
||||||
import { InvoiceStatus } from '../../@types/invoice'
|
|
||||||
import { IPaymentsService } from '../../@types/services'
|
import { IPaymentsService } from '../../@types/services'
|
||||||
|
|
||||||
const debug = createLogger('lnbits-callback-controller')
|
const debug = createLogger('lnbits-callback-controller')
|
||||||
@@ -72,8 +72,8 @@ export class LNbitsCallbackController implements IController {
|
|||||||
invoice.amountPaid = invoice.amountRequested
|
invoice.amountPaid = invoice.amountRequested
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.paymentsService.confirmInvoice(invoice)
|
await this.paymentsService.confirmInvoice(invoice as Invoice)
|
||||||
await this.paymentsService.sendInvoiceUpdateNotification(invoice)
|
await this.paymentsService.sendInvoiceUpdateNotification(invoice as Invoice)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Unable to confirm invoice ${invoice.id}`, error)
|
console.error(`Unable to confirm invoice ${invoice.id}`, error)
|
||||||
|
|
||||||
|
@@ -165,7 +165,7 @@ export class PostInvoiceController implements IController {
|
|||||||
relay_url: relayUrl,
|
relay_url: relayUrl,
|
||||||
pubkey,
|
pubkey,
|
||||||
relay_pubkey: relayPubkey,
|
relay_pubkey: relayPubkey,
|
||||||
expires_at: invoice.expiresAt?.toISOString(),
|
expires_at: invoice.expiresAt?.toISOString() ?? '',
|
||||||
invoice: invoice.bolt11,
|
invoice: invoice.bolt11,
|
||||||
amount: amount / 1000n,
|
amount: amount / 1000n,
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import { createLogger } from './logger-factory'
|
|||||||
import { createSettings } from './settings-factory'
|
import { createSettings } from './settings-factory'
|
||||||
import { IPaymentsProcessor } from '../@types/clients'
|
import { IPaymentsProcessor } from '../@types/clients'
|
||||||
import { LNbitsPaymentsProcesor } from '../payments-processors/lnbits-payment-processor'
|
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 { NullPaymentsProcessor } from '../payments-processors/null-payments-processor'
|
||||||
import { PaymentsProcessor } from '../payments-processors/payments-procesor'
|
import { PaymentsProcessor } from '../payments-processors/payments-procesor'
|
||||||
import { Settings } from '../@types/settings'
|
import { Settings } from '../@types/settings'
|
||||||
@@ -44,6 +45,19 @@ const getLNbitsAxiosConfig = (settings: Settings): CreateAxiosDefaults<any> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 createZebedeePaymentsProcessor = (settings: Settings): IPaymentsProcessor => {
|
||||||
const callbackBaseURL = path(['paymentsProcessors', 'zebedee', 'callbackBaseURL'], settings) as string | undefined
|
const callbackBaseURL = path(['paymentsProcessors', 'zebedee', 'callbackBaseURL'], settings) as string | undefined
|
||||||
if (typeof callbackBaseURL === 'undefined' || callbackBaseURL.indexOf('nostream.your-domain.com') >= 0) {
|
if (typeof callbackBaseURL === 'undefined' || callbackBaseURL.indexOf('nostream.your-domain.com') >= 0) {
|
||||||
@@ -98,6 +112,8 @@ export const createPaymentsProcessor = (): IPaymentsProcessor => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (settings.payments?.processor) {
|
switch (settings.payments?.processor) {
|
||||||
|
case 'lnurl':
|
||||||
|
return createLnurlPaymentsProcessor(settings)
|
||||||
case 'zebedee':
|
case 'zebedee':
|
||||||
return createZebedeePaymentsProcessor(settings)
|
return createZebedeePaymentsProcessor(settings)
|
||||||
case 'lnbits':
|
case 'lnbits':
|
||||||
|
69
src/payments-processors/lnurl-payments-processor.ts
Normal file
69
src/payments-processors/lnurl-payments-processor.ts
Normal file
@@ -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('lnurl-payments-processor')
|
||||||
|
|
||||||
|
export class LnurlPaymentsProcesor implements IPaymentsProcessor {
|
||||||
|
public constructor(
|
||||||
|
private httpClient: AxiosInstance,
|
||||||
|
private settings: Factory<Settings>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async getInvoice(invoice: Invoice): Promise<GetInvoiceResponse> {
|
||||||
|
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<any> {
|
||||||
|
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=${description}`)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,7 @@ export class NullPaymentsProcessor implements IPaymentsProcessor {
|
|||||||
confirmedAt: null,
|
confirmedAt: null,
|
||||||
createdAt: date,
|
createdAt: date,
|
||||||
updatedAt: date,
|
updatedAt: date,
|
||||||
|
verifyURL: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ export class NullPaymentsProcessor implements IPaymentsProcessor {
|
|||||||
rawResponse: '',
|
rawResponse: '',
|
||||||
confirmedAt: null,
|
confirmedAt: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
verifyURL: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { CreateInvoiceRequest, CreateInvoiceResponse, IPaymentsProcessor } from '../@types/clients'
|
import { CreateInvoiceRequest, CreateInvoiceResponse, GetInvoiceResponse, IPaymentsProcessor } from '../@types/clients'
|
||||||
import { Invoice } from '../@types/invoice'
|
import { Invoice } from '../@types/invoice'
|
||||||
|
|
||||||
export class PaymentsProcessor implements IPaymentsProcessor {
|
export class PaymentsProcessor implements IPaymentsProcessor {
|
||||||
@@ -6,8 +6,8 @@ export class PaymentsProcessor implements IPaymentsProcessor {
|
|||||||
private readonly processor: IPaymentsProcessor
|
private readonly processor: IPaymentsProcessor
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async getInvoice(invoiceId: string): Promise<Invoice> {
|
public async getInvoice(invoice: string | Invoice): Promise<GetInvoiceResponse> {
|
||||||
return this.processor.getInvoice(invoiceId)
|
return this.processor.getInvoice(invoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createInvoice(request: CreateInvoiceRequest): Promise<CreateInvoiceResponse> {
|
public async createInvoice(request: CreateInvoiceRequest): Promise<CreateInvoiceResponse> {
|
||||||
|
@@ -3,7 +3,6 @@ import {
|
|||||||
applySpec,
|
applySpec,
|
||||||
ifElse,
|
ifElse,
|
||||||
is,
|
is,
|
||||||
isNil,
|
|
||||||
omit,
|
omit,
|
||||||
pipe,
|
pipe,
|
||||||
prop,
|
prop,
|
||||||
@@ -92,17 +91,10 @@ export class InvoiceRepository implements IInvoiceRepository {
|
|||||||
status: prop('status'),
|
status: prop('status'),
|
||||||
description: prop('description'),
|
description: prop('description'),
|
||||||
// confirmed_at: prop('confirmedAt'),
|
// confirmed_at: prop('confirmedAt'),
|
||||||
expires_at: ifElse(
|
expires_at: prop('expiresAt'),
|
||||||
propSatisfies(isNil, 'expiresAt'),
|
|
||||||
always(undefined),
|
|
||||||
prop('expiresAt'),
|
|
||||||
),
|
|
||||||
updated_at: always(new Date()),
|
updated_at: always(new Date()),
|
||||||
created_at: ifElse(
|
created_at: prop('createdAt'),
|
||||||
propSatisfies(isNil, 'createdAt'),
|
verify_url: prop('verifyURL'),
|
||||||
always(undefined),
|
|
||||||
prop('createdAt'),
|
|
||||||
),
|
|
||||||
})(invoice)
|
})(invoice)
|
||||||
|
|
||||||
debug('row: %o', row)
|
debug('row: %o', row)
|
||||||
@@ -120,6 +112,7 @@ export class InvoiceRepository implements IInvoiceRepository {
|
|||||||
'description',
|
'description',
|
||||||
'expires_at',
|
'expires_at',
|
||||||
'created_at',
|
'created_at',
|
||||||
|
'verify_url',
|
||||||
])(row)
|
])(row)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -36,10 +36,12 @@ export class PaymentsService implements IPaymentsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getInvoiceFromPaymentsProcessor(invoiceId: string): Promise<Invoice> {
|
public async getInvoiceFromPaymentsProcessor(invoice: Invoice): Promise<Partial<Invoice>> {
|
||||||
debug('get invoice %s from payment processor', invoiceId)
|
debug('get invoice %s from payment processor', invoice.id)
|
||||||
try {
|
try {
|
||||||
return await this.paymentsProcessor.getInvoice(invoiceId)
|
return await this.paymentsProcessor.getInvoice(
|
||||||
|
this.settings().payments?.processor === 'lnurl' ? invoice : invoice.id
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Unable to get invoice from payments processor. Reason:', error)
|
console.log('Unable to get invoice from payments processor. Reason:', error)
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ export class PaymentsService implements IPaymentsService {
|
|||||||
expiresAt: invoiceResponse.expiresAt,
|
expiresAt: invoiceResponse.expiresAt,
|
||||||
updatedAt: date,
|
updatedAt: date,
|
||||||
createdAt: date,
|
createdAt: date,
|
||||||
|
verifyURL: invoiceResponse.verifyURL,
|
||||||
},
|
},
|
||||||
transaction.transaction,
|
transaction.transaction,
|
||||||
)
|
)
|
||||||
@@ -99,6 +102,7 @@ export class PaymentsService implements IPaymentsService {
|
|||||||
expiresAt: invoiceResponse.expiresAt,
|
expiresAt: invoiceResponse.expiresAt,
|
||||||
updatedAt: date,
|
updatedAt: date,
|
||||||
createdAt: invoiceResponse.createdAt,
|
createdAt: invoiceResponse.createdAt,
|
||||||
|
verifyURL: invoiceResponse.verifyURL,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback()
|
await transaction.rollback()
|
||||||
@@ -108,7 +112,7 @@ export class PaymentsService implements IPaymentsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateInvoice(invoice: Invoice): Promise<void> {
|
public async updateInvoice(invoice: Partial<Invoice>): Promise<void> {
|
||||||
debug('update invoice %s: %o', invoice.id, invoice)
|
debug('update invoice %s: %o', invoice.id, invoice)
|
||||||
try {
|
try {
|
||||||
await this.invoiceRepository.upsert({
|
await this.invoiceRepository.upsert({
|
||||||
@@ -129,6 +133,21 @@ export class PaymentsService implements IPaymentsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async updateInvoiceStatus(invoice: Partial<Invoice>): Promise<void> {
|
||||||
|
debug('update invoice %s: %o', invoice.id, invoice)
|
||||||
|
try {
|
||||||
|
const fullInvoice = await this.invoiceRepository.findById(invoice.id)
|
||||||
|
await this.invoiceRepository.upsert({
|
||||||
|
...fullInvoice,
|
||||||
|
status: invoice.status,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Unable to update invoice. Reason:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async confirmInvoice(
|
public async confirmInvoice(
|
||||||
invoice: Invoice,
|
invoice: Invoice,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
@@ -31,6 +31,7 @@ export const fromDBInvoice = applySpec<Invoice>({
|
|||||||
expiresAt: prop('expires_at'),
|
expiresAt: prop('expires_at'),
|
||||||
updatedAt: prop('updated_at'),
|
updatedAt: prop('updated_at'),
|
||||||
createdAt: prop('created_at'),
|
createdAt: prop('created_at'),
|
||||||
|
verifyURL: prop('verify_url'),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const fromDBUser = applySpec<User>({
|
export const fromDBUser = applySpec<User>({
|
||||||
|
Reference in New Issue
Block a user