michangarrito/apps/backend/dist/modules/codi-spei/codi-spei.service.js
rckrdmrd 97f407c661 [MIGRATION-V2] feat: Migrar michangarrito a estructura v2
- Prefijo v2: MCH
- TRACEABILITY-MASTER.yml creado
- Listo para integracion como submodulo

Workspace: v2.0.0 | SIMCO: v4.0.0
2026-01-10 11:28:54 -06:00

193 lines
8.2 KiB
JavaScript

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodiSpeiService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const virtual_account_entity_1 = require("./entities/virtual-account.entity");
const codi_transaction_entity_1 = require("./entities/codi-transaction.entity");
const spei_transaction_entity_1 = require("./entities/spei-transaction.entity");
let CodiSpeiService = class CodiSpeiService {
constructor(virtualAccountRepo, codiRepo, speiRepo, dataSource) {
this.virtualAccountRepo = virtualAccountRepo;
this.codiRepo = codiRepo;
this.speiRepo = speiRepo;
this.dataSource = dataSource;
}
async getVirtualAccount(tenantId) {
return this.virtualAccountRepo.findOne({
where: { tenantId, status: virtual_account_entity_1.VirtualAccountStatus.ACTIVE },
});
}
async createVirtualAccount(tenantId, beneficiaryName, provider = 'stp') {
const existing = await this.getVirtualAccount(tenantId);
if (existing) {
return existing;
}
const mockClabe = `646180${Math.floor(Math.random() * 1000000000000).toString().padStart(12, '0')}`;
const account = this.virtualAccountRepo.create({
tenantId,
provider,
clabe: mockClabe,
beneficiaryName,
status: virtual_account_entity_1.VirtualAccountStatus.ACTIVE,
});
return this.virtualAccountRepo.save(account);
}
async generateQr(tenantId, dto) {
const result = await this.dataSource.query(`SELECT generate_codi_reference($1) as reference`, [tenantId]);
const reference = result[0].reference;
const expiresAt = new Date();
expiresAt.setMinutes(expiresAt.getMinutes() + 5);
const qrData = JSON.stringify({
type: 'codi',
amount: dto.amount,
reference,
merchant: tenantId,
expires: expiresAt.toISOString(),
});
const transaction = this.codiRepo.create({
tenantId,
saleId: dto.saleId,
qrData,
amount: dto.amount,
reference,
description: dto.description || `Cobro ${reference}`,
status: codi_transaction_entity_1.CodiTransactionStatus.PENDING,
expiresAt,
});
return this.codiRepo.save(transaction);
}
async getCodiStatus(id) {
const transaction = await this.codiRepo.findOne({ where: { id } });
if (!transaction) {
throw new common_1.NotFoundException('Transaccion CoDi no encontrada');
}
if (transaction.status === codi_transaction_entity_1.CodiTransactionStatus.PENDING &&
new Date() > transaction.expiresAt) {
transaction.status = codi_transaction_entity_1.CodiTransactionStatus.EXPIRED;
await this.codiRepo.save(transaction);
}
return transaction;
}
async confirmCodi(id, providerData) {
const transaction = await this.getCodiStatus(id);
if (transaction.status !== codi_transaction_entity_1.CodiTransactionStatus.PENDING) {
throw new common_1.BadRequestException(`Transaccion no esta pendiente: ${transaction.status}`);
}
transaction.status = codi_transaction_entity_1.CodiTransactionStatus.CONFIRMED;
transaction.confirmedAt = new Date();
transaction.providerResponse = providerData;
return this.codiRepo.save(transaction);
}
async getCodiTransactions(tenantId, limit = 50) {
return this.codiRepo.find({
where: { tenantId },
order: { createdAt: 'DESC' },
take: limit,
});
}
async getSpeiTransactions(tenantId, limit = 50) {
return this.speiRepo.find({
where: { tenantId },
order: { receivedAt: 'DESC' },
take: limit,
});
}
async receiveSpei(tenantId, data) {
const account = await this.getVirtualAccount(tenantId);
const transaction = this.speiRepo.create({
tenantId,
virtualAccountId: account?.id,
amount: data.amount,
senderClabe: data.senderClabe,
senderName: data.senderName,
senderRfc: data.senderRfc,
senderBank: data.senderBank,
reference: data.reference,
trackingKey: data.trackingKey,
status: spei_transaction_entity_1.SpeiTransactionStatus.RECEIVED,
receivedAt: new Date(),
providerData: data.providerData,
});
return this.speiRepo.save(transaction);
}
async reconcileSpei(id, saleId) {
const transaction = await this.speiRepo.findOne({ where: { id } });
if (!transaction) {
throw new common_1.NotFoundException('Transaccion SPEI no encontrada');
}
transaction.saleId = saleId;
transaction.status = spei_transaction_entity_1.SpeiTransactionStatus.RECONCILED;
transaction.reconciledAt = new Date();
return this.speiRepo.save(transaction);
}
async getSummary(tenantId, date) {
const targetDate = date || new Date();
const dateStr = targetDate.toISOString().split('T')[0];
const result = await this.dataSource.query(`SELECT * FROM get_codi_spei_summary($1, $2::date)`, [tenantId, dateStr]);
return result[0] || {
codi_count: 0,
codi_total: 0,
spei_count: 0,
spei_total: 0,
};
}
async handleCodiWebhook(payload) {
const { reference, status, transactionId } = payload;
const transaction = await this.codiRepo.findOne({
where: { reference },
});
if (!transaction) {
throw new common_1.NotFoundException('Transaccion no encontrada');
}
if (status === 'confirmed') {
await this.confirmCodi(transaction.id, {
providerTransactionId: transactionId,
...payload,
});
}
}
async handleSpeiWebhook(clabe, payload) {
const account = await this.virtualAccountRepo.findOne({
where: { clabe },
});
if (!account) {
throw new common_1.NotFoundException('Cuenta virtual no encontrada');
}
await this.receiveSpei(account.tenantId, {
amount: payload.amount,
senderClabe: payload.senderClabe,
senderName: payload.senderName,
senderRfc: payload.senderRfc,
senderBank: payload.senderBank,
reference: payload.reference,
trackingKey: payload.trackingKey,
providerData: payload,
});
}
};
exports.CodiSpeiService = CodiSpeiService;
exports.CodiSpeiService = CodiSpeiService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(virtual_account_entity_1.VirtualAccount)),
__param(1, (0, typeorm_1.InjectRepository)(codi_transaction_entity_1.CodiTransaction)),
__param(2, (0, typeorm_1.InjectRepository)(spei_transaction_entity_1.SpeiTransaction)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.DataSource])
], CodiSpeiService);
//# sourceMappingURL=codi-spei.service.js.map