erp-core-backend-v2/src/modules/payment-terminals/controllers/mercadopago.controller.ts
Adrian Flores Cortes fd8a0a508e [TASK-2026-01-25-ERP-INTEGRACIONES] feat: Add payment terminals + AI role-based access
Payment Terminals (MercadoPago + Clip):
- TenantTerminalConfig, TerminalPayment, TerminalWebhookEvent entities
- MercadoPagoService: payments, refunds, links, webhooks
- ClipService: payments, refunds, links, webhooks
- Controllers for authenticated and webhook endpoints
- Retry with exponential backoff
- Multi-tenant credential management

AI Role-Based Access:
- ERPRole config: ADMIN, SUPERVISOR, OPERATOR, CUSTOMER
- 70+ tools mapped to roles
- System prompts per role (admin, supervisor, operator, customer)
- RoleBasedAIService with tool filtering
- OpenRouter integration
- Rate limiting per role

Based on: michangarrito INT-004, INT-005, MCH-012, MCH-013

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 01:32:32 -06:00

166 lines
4.3 KiB
TypeScript

/**
* MercadoPago Controller
*
* Endpoints para pagos con MercadoPago
*/
import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { MercadoPagoService } from '../services/mercadopago.service';
export class MercadoPagoController {
public router: Router;
private mercadoPagoService: MercadoPagoService;
constructor(private dataSource: DataSource) {
this.router = Router();
this.mercadoPagoService = new MercadoPagoService(dataSource);
this.initializeRoutes();
}
private initializeRoutes(): void {
// Pagos
this.router.post('/payments', this.createPayment.bind(this));
this.router.get('/payments/:id', this.getPayment.bind(this));
this.router.post('/payments/:id/refund', this.refundPayment.bind(this));
// Links de pago
this.router.post('/links', this.createPaymentLink.bind(this));
}
/**
* POST /mercadopago/payments
* Crear un nuevo pago
*/
private async createPayment(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const tenantId = this.getTenantId(req);
const userId = this.getUserId(req);
const payment = await this.mercadoPagoService.createPayment(
tenantId,
{
amount: req.body.amount,
currency: req.body.currency,
description: req.body.description,
paymentMethod: req.body.paymentMethod,
customerEmail: req.body.customerEmail,
customerName: req.body.customerName,
referenceType: req.body.referenceType,
referenceId: req.body.referenceId,
metadata: req.body.metadata,
},
userId
);
res.status(201).json({
success: true,
data: this.sanitizePayment(payment),
});
} catch (error) {
next(error);
}
}
/**
* GET /mercadopago/payments/:id
* Obtener estado de un pago
*/
private async getPayment(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const tenantId = this.getTenantId(req);
const paymentId = req.params.id;
const payment = await this.mercadoPagoService.getPayment(tenantId, paymentId);
res.json({
success: true,
data: this.sanitizePayment(payment),
});
} catch (error) {
next(error);
}
}
/**
* POST /mercadopago/payments/:id/refund
* Reembolsar un pago
*/
private async refundPayment(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const tenantId = this.getTenantId(req);
const paymentId = req.params.id;
const payment = await this.mercadoPagoService.refundPayment(tenantId, {
paymentId,
amount: req.body.amount,
reason: req.body.reason,
});
res.json({
success: true,
data: this.sanitizePayment(payment),
});
} catch (error) {
next(error);
}
}
/**
* POST /mercadopago/links
* Crear un link de pago
*/
private async createPaymentLink(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const tenantId = this.getTenantId(req);
const userId = this.getUserId(req);
const link = await this.mercadoPagoService.createPaymentLink(
tenantId,
{
amount: req.body.amount,
title: req.body.title,
description: req.body.description,
expiresAt: req.body.expiresAt ? new Date(req.body.expiresAt) : undefined,
referenceType: req.body.referenceType,
referenceId: req.body.referenceId,
},
userId
);
res.status(201).json({
success: true,
data: link,
});
} catch (error) {
next(error);
}
}
/**
* Obtener tenant ID del request
*/
private getTenantId(req: Request): string {
const tenantId = (req as any).tenantId || (req as any).user?.tenantId;
if (!tenantId) {
throw new Error('Tenant ID not found in request');
}
return tenantId;
}
/**
* Obtener user ID del request
*/
private getUserId(req: Request): string | undefined {
return (req as any).userId || (req as any).user?.id;
}
/**
* Sanitizar pago para respuesta (ocultar datos sensibles)
*/
private sanitizePayment(payment: any): any {
const { providerResponse, ...safe } = payment;
return safe;
}
}