Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
237 lines
4.5 KiB
Markdown
237 lines
4.5 KiB
Markdown
---
|
|
id: "ET-PAY-004"
|
|
title: "API REST Payments"
|
|
type: "Technical Specification"
|
|
status: "Done"
|
|
priority: "Alta"
|
|
epic: "OQI-005"
|
|
project: "trading-platform"
|
|
version: "1.0.0"
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# ET-PAY-004: API REST Payments
|
|
|
|
**Epic:** OQI-005 Pagos y Stripe
|
|
**Versión:** 1.0
|
|
**Fecha:** 2025-12-05
|
|
|
|
---
|
|
|
|
## 1. Endpoints Payments
|
|
|
|
### POST `/api/v1/payments/intent`
|
|
Crea Payment Intent para pago one-time
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"amount": 99.99,
|
|
"payment_method_id": "pm_...",
|
|
"description": "Premium course purchase"
|
|
}
|
|
```
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"payment_intent_id": "pi_...",
|
|
"client_secret": "pi_..._secret_...",
|
|
"status": "requires_confirmation"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET `/api/v1/payments`
|
|
Lista pagos del usuario
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"payments": [
|
|
{
|
|
"id": "uuid",
|
|
"amount": 99.99,
|
|
"status": "succeeded",
|
|
"description": "Premium course",
|
|
"created_at": "2025-01-20T10:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### POST `/api/v1/payments/:id/refund`
|
|
Solicita reembolso (admin)
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"reason": "requested_by_customer",
|
|
"amount": 99.99
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Endpoints Subscriptions
|
|
|
|
### POST `/api/v1/subscriptions`
|
|
Crea suscripción
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"price_id": "price_...",
|
|
"payment_method_id": "pm_..."
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET `/api/v1/subscriptions`
|
|
Lista suscripciones activas
|
|
|
|
---
|
|
|
|
### DELETE `/api/v1/subscriptions/:id`
|
|
Cancela suscripción
|
|
|
|
**Query:** `?immediate=false`
|
|
|
|
---
|
|
|
|
## 3. Endpoints Payment Methods
|
|
|
|
### POST `/api/v1/payment-methods`
|
|
Guarda método de pago
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"payment_method_id": "pm_..."
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET `/api/v1/payment-methods`
|
|
Lista métodos guardados
|
|
|
|
---
|
|
|
|
### DELETE `/api/v1/payment-methods/:id`
|
|
Elimina método de pago
|
|
|
|
---
|
|
|
|
## 4. Implementation
|
|
|
|
```typescript
|
|
// src/modules/payments/payment.controller.ts
|
|
|
|
export class PaymentController {
|
|
async createPaymentIntent(req: Request, res: Response) {
|
|
const { amount, payment_method_id, description } = req.body;
|
|
const userId = req.user!.id;
|
|
|
|
const result = await paymentService.createPaymentIntent({
|
|
user_id: userId,
|
|
amount,
|
|
payment_method_id,
|
|
description,
|
|
});
|
|
|
|
return successResponse(res, result, 200);
|
|
}
|
|
|
|
async getPayments(req: Request, res: Response) {
|
|
const userId = req.user!.id;
|
|
const { status, limit = 20, offset = 0 } = req.query;
|
|
|
|
const payments = await paymentService.getPayments({
|
|
user_id: userId,
|
|
status,
|
|
limit: Number(limit),
|
|
offset: Number(offset),
|
|
});
|
|
|
|
return successResponse(res, payments, 200);
|
|
}
|
|
|
|
async requestRefund(req: Request, res: Response) {
|
|
const { id } = req.params;
|
|
const { reason, amount } = req.body;
|
|
|
|
const refund = await paymentService.createRefund({
|
|
payment_id: id,
|
|
reason,
|
|
amount,
|
|
});
|
|
|
|
return successResponse(res, refund, 200);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Validations
|
|
|
|
```typescript
|
|
// Zod schemas
|
|
export const createPaymentIntentSchema = z.object({
|
|
amount: z.number().positive(),
|
|
payment_method_id: z.string().min(1),
|
|
description: z.string().optional(),
|
|
});
|
|
|
|
export const createSubscriptionSchema = z.object({
|
|
price_id: z.string().startsWith('price_'),
|
|
payment_method_id: z.string().min(1),
|
|
trial_days: z.number().optional(),
|
|
});
|
|
|
|
export const refundSchema = z.object({
|
|
reason: z.enum(['duplicate', 'fraudulent', 'requested_by_customer']),
|
|
amount: z.number().positive().optional(),
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Routes
|
|
|
|
```typescript
|
|
// src/modules/payments/payment.routes.ts
|
|
|
|
const router = Router();
|
|
|
|
// Payments
|
|
router.post('/intent', authenticate, validate(createPaymentIntentSchema), controller.createPaymentIntent);
|
|
router.get('/', authenticate, controller.getPayments);
|
|
router.post('/:id/refund', authenticate, requireAdmin, validate(refundSchema), controller.requestRefund);
|
|
|
|
// Subscriptions
|
|
router.post('/subscriptions', authenticate, validate(createSubscriptionSchema), subscriptionController.create);
|
|
router.get('/subscriptions', authenticate, subscriptionController.list);
|
|
router.delete('/subscriptions/:id', authenticate, subscriptionController.cancel);
|
|
|
|
// Payment Methods
|
|
router.post('/payment-methods', authenticate, paymentMethodController.save);
|
|
router.get('/payment-methods', authenticate, paymentMethodController.list);
|
|
router.delete('/payment-methods/:id', authenticate, paymentMethodController.delete);
|
|
router.patch('/payment-methods/:id/default', authenticate, paymentMethodController.setDefault);
|
|
|
|
export default router;
|
|
```
|