Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
222 lines
5.5 KiB
JavaScript
222 lines
5.5 KiB
JavaScript
import express from 'express';
|
|
import { transactionService } from '../services/transactionService.js';
|
|
import { authenticate, requireTenant, requireAdmin } from '../middleware/auth.js';
|
|
import { validateRequest } from '../middleware/validation.js';
|
|
import { body, param, query } from 'express-validator';
|
|
import { logger } from '../utils/logger.js';
|
|
|
|
const router = express.Router();
|
|
|
|
// Get transactions
|
|
router.get('/',
|
|
authenticate,
|
|
requireTenant,
|
|
validateRequest([
|
|
query('type').optional().isIn(['payment', 'refund', 'adjustment', 'fee']),
|
|
query('status').optional().isIn(['pending', 'processing', 'succeeded', 'failed', 'canceled']),
|
|
query('startDate').optional().isISO8601(),
|
|
query('endDate').optional().isISO8601(),
|
|
query('limit').optional().isInt({ min: 1, max: 100 }),
|
|
query('offset').optional().isInt({ min: 0 })
|
|
]),
|
|
async (req, res) => {
|
|
try {
|
|
const transactions = await transactionService.getTransactions(
|
|
req.tenantId,
|
|
req.query
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
transactions
|
|
});
|
|
} catch (error) {
|
|
logger.error('Get transactions error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to get transactions'
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
// Get transaction by ID
|
|
router.get('/:id',
|
|
authenticate,
|
|
requireTenant,
|
|
validateRequest([
|
|
param('id').isMongoId()
|
|
]),
|
|
async (req, res) => {
|
|
try {
|
|
const transaction = await transactionService.getTransaction(
|
|
req.tenantId,
|
|
req.params.id
|
|
);
|
|
|
|
if (!transaction) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
error: 'Transaction not found'
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
transaction
|
|
});
|
|
} catch (error) {
|
|
logger.error('Get transaction error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to get transaction'
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
// Create refund
|
|
router.post('/:id/refund',
|
|
authenticate,
|
|
requireTenant,
|
|
validateRequest([
|
|
param('id').isMongoId(),
|
|
body('amount').optional().isFloat({ min: 0 }),
|
|
body('reason').notEmpty(),
|
|
body('metadata').optional().isObject()
|
|
]),
|
|
async (req, res) => {
|
|
try {
|
|
const refund = await transactionService.createRefund(
|
|
req.tenantId,
|
|
req.params.id,
|
|
{
|
|
...req.body,
|
|
initiatedBy: req.user.id
|
|
}
|
|
);
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
refund,
|
|
message: 'Refund initiated successfully'
|
|
});
|
|
} catch (error) {
|
|
logger.error('Create refund error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to create refund'
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
// Get transaction summary
|
|
router.get('/summary/:period',
|
|
authenticate,
|
|
requireTenant,
|
|
validateRequest([
|
|
param('period').isIn(['daily', 'weekly', 'monthly', 'yearly']),
|
|
query('startDate').optional().isISO8601(),
|
|
query('endDate').optional().isISO8601()
|
|
]),
|
|
async (req, res) => {
|
|
try {
|
|
const summary = await transactionService.getTransactionSummary(
|
|
req.tenantId,
|
|
req.params.period,
|
|
{
|
|
startDate: req.query.startDate,
|
|
endDate: req.query.endDate
|
|
}
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
summary
|
|
});
|
|
} catch (error) {
|
|
logger.error('Get transaction summary error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to get transaction summary'
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
// Export transactions
|
|
router.get('/export/:format',
|
|
authenticate,
|
|
requireTenant,
|
|
validateRequest([
|
|
param('format').isIn(['csv', 'pdf', 'excel']),
|
|
query('startDate').optional().isISO8601(),
|
|
query('endDate').optional().isISO8601()
|
|
]),
|
|
async (req, res) => {
|
|
try {
|
|
const { filename, data } = await transactionService.exportTransactions(
|
|
req.tenantId,
|
|
req.params.format,
|
|
{
|
|
startDate: req.query.startDate,
|
|
endDate: req.query.endDate
|
|
}
|
|
);
|
|
|
|
// Set appropriate headers based on format
|
|
const contentType = {
|
|
csv: 'text/csv',
|
|
pdf: 'application/pdf',
|
|
excel: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
};
|
|
|
|
res.setHeader('Content-Type', contentType[req.params.format]);
|
|
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
res.send(data);
|
|
} catch (error) {
|
|
logger.error('Export transactions error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to export transactions'
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
// Admin: Create manual adjustment
|
|
router.post('/adjustment',
|
|
authenticate,
|
|
requireAdmin,
|
|
validateRequest([
|
|
body('tenantId').isMongoId(),
|
|
body('type').isIn(['credit', 'debit']),
|
|
body('amount').isFloat({ min: 0 }),
|
|
body('currency').optional().isString(),
|
|
body('reason').notEmpty(),
|
|
body('metadata').optional().isObject()
|
|
]),
|
|
async (req, res) => {
|
|
try {
|
|
const adjustment = await transactionService.createAdjustment({
|
|
...req.body,
|
|
createdBy: req.user.id
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
adjustment,
|
|
message: 'Adjustment created successfully'
|
|
});
|
|
} catch (error) {
|
|
logger.error('Create adjustment error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to create adjustment'
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
export default router; |