Enhance payment processing and rental features
- Updated the BTCPay service to support internal Lightning invoices with private route hints, improving payment routing for users with private channels. - Added reconciliation methods for pending rents and subscriptions to ensure missed payments are processed on startup. - Enhanced the rental and subscription services to handle payments in satoshis, aligning with Lightning Network standards. - Improved the rental modal and content detail components to display rental status and pricing more clearly, including a countdown for rental expiration. - Refactored various components to streamline user experience and ensure accurate rental access checks.
This commit is contained in:
@@ -24,7 +24,7 @@ export class SeasonRent {
|
||||
userId: string;
|
||||
|
||||
@Column('decimal', {
|
||||
precision: 5,
|
||||
precision: 15,
|
||||
scale: 2,
|
||||
transformer: new ColumnNumericTransformer(),
|
||||
})
|
||||
|
||||
@@ -2,11 +2,12 @@ import {
|
||||
BadRequestException,
|
||||
Inject,
|
||||
Injectable,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
UnprocessableEntityException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { IsNull, Repository } from 'typeorm';
|
||||
import { IsNull, MoreThanOrEqual, Not, Repository } from 'typeorm';
|
||||
import { SeasonRent } from './entities/season-rents.entity';
|
||||
import { SeasonService } from './season.service';
|
||||
import { BTCPayService } from 'src/payment/providers/services/btcpay.service';
|
||||
@@ -215,6 +216,49 @@ export class SeasonRentsService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile pending season rents against BTCPay on startup.
|
||||
*/
|
||||
async reconcilePendingPayments(): Promise<number> {
|
||||
const cutoff = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000);
|
||||
const pendingRents = await this.seasonRentRepository.find({
|
||||
where: {
|
||||
status: 'pending',
|
||||
providerId: Not(IsNull()),
|
||||
createdAt: MoreThanOrEqual(cutoff),
|
||||
},
|
||||
});
|
||||
|
||||
if (pendingRents.length === 0) return 0;
|
||||
|
||||
Logger.log(
|
||||
`Reconciling ${pendingRents.length} pending season rent(s)…`,
|
||||
'SeasonRentsService',
|
||||
);
|
||||
|
||||
let settled = 0;
|
||||
for (const rent of pendingRents) {
|
||||
try {
|
||||
const invoice = await this.btcpayService.getInvoice(rent.providerId);
|
||||
if (invoice.state === 'PAID') {
|
||||
await this.lightningPaid(rent.providerId, invoice);
|
||||
settled++;
|
||||
Logger.log(
|
||||
`Reconciled season rent ${rent.id} (invoice ${rent.providerId}) — now paid`,
|
||||
'SeasonRentsService',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.warn(
|
||||
`Reconciliation failed for season rent ${rent.id}: ${err.message}`,
|
||||
'SeasonRentsService',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return settled;
|
||||
}
|
||||
|
||||
private getExpiringDate() {
|
||||
return new Date(Date.now() - 2 * 24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user