michangarrito/docs/02-integraciones/INT-005-clip.md
rckrdmrd 15852f2d6a [MCH-DOC-VAL] docs: Mejorar integraciones INT-004, INT-005, INT-006 con SIMCO 4.0.1
Actualiza documentación de integraciones de pagos a estructura estándar
de 11-13 secciones siguiendo template de INT-001.

Cambios por integración:

INT-004-mercadopago.md (+458 líneas):
- Actualizado simco_version a 4.0.1
- Agregado: Rate Limits con retry strategy
- Agregado: Manejo de Errores completo (8 códigos)
- Agregado: Fallbacks y modo degradado
- Mejorado: Multi-tenant con SQL schema
- Agregado: Webhooks IPN con validación de firma
- Agregado: Testing con tarjetas de prueba MX
- Agregado: Monitoreo con métricas y logs

INT-005-clip.md (+485 líneas):
- Actualizado simco_version a 4.0.1
- Agregado: Rate Limits (100 req/min)
- Agregado: Manejo de Errores (7 códigos)
- Agregado: Fallbacks con cola Redis
- Agregado: Multi-tenant con tenant_clip_config
- Agregado: Webhooks con HMAC validation
- Agregado: Testing con tarjetas Clip MX
- Agregado: Monitoreo y Referencias

INT-006-codi-banxico.md (+694 líneas):
- Actualizado simco_version a 4.0.1
- Agregado: Rate Limits STP/Banxico
- Agregado: Manejo de Errores CoDi/STP
- Agregado: Fallbacks (QR alternativo, manual)
- Agregado: Multi-tenant con CLABEs por tenant
- Agregado: Webhooks STP con firma RSA-SHA256
- Agregado: Testing con CLABEs sandbox
- Agregado: Monitoreo y normatividad mexicana

Total: +1,573 líneas de documentación técnica.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 04:46:57 -06:00

590 lines
18 KiB
Markdown

---
id: INT-005
type: Integration
title: "Terminal de pagos Clip"
provider: "Clip México"
status: Mock
integration_type: "Pagos con tarjeta"
created_at: 2026-01-10
updated_at: 2026-01-17
simco_version: "4.0.1"
tags:
- pagos
- tarjeta
- clip
- pos
- fintech
---
# INT-005: Terminal de pagos Clip
## Metadata
| Campo | Valor |
|-------|-------|
| **Codigo** | INT-005 |
| **Proveedor** | Clip México |
| **Tipo** | Pagos con tarjeta |
| **Estado** | Mock (pendiente de implementar) |
| **Multi-tenant** | Si |
| **Fecha planeada** | 2026-Q1 |
| **Owner** | Backend Team |
---
## 1. Descripcion
Integracion con el sistema de terminales punto de venta (TPV) de Clip Mexico para procesar pagos con tarjeta de credito y debito. Clip es una de las soluciones de pago mas populares en Mexico para pequenos comercios, permitiendo aceptar pagos con tarjeta sin necesidad de una cuenta bancaria empresarial tradicional.
**Casos de uso principales:**
- Cobro presencial con tarjeta de credito/debito en el changarro
- Generacion de links de pago para cobros a distancia
- Consulta de historial de transacciones y conciliacion
- Gestion de reembolsos y cancelaciones
- Reportes de ventas por periodo
---
## 2. Credenciales Requeridas
### Variables de Entorno
| Variable | Descripcion | Tipo | Obligatorio |
|----------|-------------|------|-------------|
| CLIP_API_KEY | Llave de API proporcionada por Clip | string | SI |
| CLIP_SECRET_KEY | Llave secreta para firmar requests | string | SI |
| CLIP_MERCHANT_ID | Identificador unico del comercio en Clip | string | SI |
| CLIP_WEBHOOK_SECRET | Secret para validar webhooks de Clip | string | SI |
| CLIP_ENVIRONMENT | Ambiente (sandbox/production) | string | SI |
### Ejemplo de .env
```env
# Terminal de pagos Clip
CLIP_API_KEY=clip_api_xxxxxxxxxxxxxxxx
CLIP_SECRET_KEY=clip_secret_xxxxxxxxxxxxxxxx
CLIP_MERCHANT_ID=mer_xxxxxxxxxxxxxxxx
CLIP_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxx
CLIP_ENVIRONMENT=sandbox
```
### Obtencion de Credenciales
1. Crear cuenta en [Clip Dashboard](https://dashboard.clip.mx/)
2. Acceder a la seccion de Desarrolladores
3. Generar API Key y Secret Key
4. Obtener Merchant ID de la configuracion de cuenta
5. Configurar Webhook URL y obtener Webhook Secret
---
## 3. Arquitectura
```
┌─────────────────────────────────────────────────────────────────┐
│ MiChangarrito │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Frontend │───▶│ API Server │───▶│ ClipService │ │
│ │ (Ventas) │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └────────┬─────────┘ │
└────────────────────────────────────────────────────┼────────────┘
┌──────────────────┐
│ Clip API │
│ (México) │
│ │
│ - Pagos │
│ - Reembolsos │
│ - Consultas │
└──────────────────┘
```
### Flujo de Pago
1. Usuario selecciona "Pagar con tarjeta" en el punto de venta
2. Backend crea una sesion de pago en Clip API
3. Se genera un link/QR para completar el pago
4. Cliente realiza el pago con su tarjeta
5. Clip envia webhook de confirmacion
6. Backend actualiza estado de la venta
---
## 4. Endpoints
### Endpoints Consumidos (Clip API)
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| POST | `/v1/payments` | Crear un nuevo pago |
| GET | `/v1/payments/{id}` | Consultar estado de pago |
| POST | `/v1/payments/{id}/refund` | Procesar reembolso |
| GET | `/v1/transactions` | Listar transacciones |
| GET | `/v1/merchants/{id}/balance` | Consultar balance |
| POST | `/v1/payment-links` | Crear link de pago |
| GET | `/v1/payment-links/{id}` | Consultar link de pago |
### Endpoints Expuestos (MiChangarrito)
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| POST | `/api/v1/payments/clip/create` | Iniciar pago con Clip |
| GET | `/api/v1/payments/clip/{id}` | Consultar pago |
| POST | `/api/v1/payments/clip/{id}/refund` | Solicitar reembolso |
| POST | `/api/v1/payments/clip/payment-link` | Crear link de pago |
| POST | `/api/v1/webhooks/clip` | Recibir notificaciones de Clip |
---
## 5. Rate Limits
| Limite | Valor | Periodo | Accion si excede |
|--------|-------|---------|------------------|
| Requests API | 100 | por minuto | 429 + Retry-After |
| Crear pagos | 60 | por minuto | 429 + backoff |
| Consultas | 200 | por minuto | 429 + Retry-After |
| Webhooks | Sin limite | - | N/A |
### Estrategia de Retry
```typescript
const RETRY_DELAYS = [1000, 2000, 4000, 8000];
async function executeWithRetry<T>(
operation: () => Promise<T>,
maxRetries = 4
): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (error.response?.status === 429 && attempt < maxRetries - 1) {
const retryAfter = error.response.headers['retry-after'];
const delay = retryAfter
? parseInt(retryAfter) * 1000
: RETRY_DELAYS[attempt];
await sleep(delay);
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
```
---
## 6. Manejo de Errores
### Codigos de Error
| Codigo | Descripcion | Accion Recomendada | Retry |
|--------|-------------|-------------------|-------|
| 400 | Request invalido | Validar parametros enviados | NO |
| 401 | Credenciales invalidas | Verificar API Key y Secret | NO |
| 402 | Pago rechazado | Informar al usuario, ofrecer alternativa | NO |
| 403 | Operacion no permitida | Verificar permisos del merchant | NO |
| 404 | Recurso no encontrado | Verificar ID de pago/transaccion | NO |
| 429 | Rate limit excedido | Esperar y reintentar con backoff | SI |
| 500 | Error interno de Clip | Reintentar con backoff exponencial | SI |
### Ejemplo de Manejo
```typescript
import { Injectable, Logger, BadRequestException } from '@nestjs/common';
@Injectable()
export class ClipService {
private readonly logger = new Logger(ClipService.name);
async createPayment(params: CreatePaymentDto, tenantId: string): Promise<ClipPayment> {
try {
const response = await this.clipClient.post('/v1/payments', params);
this.logger.log('Clip payment created', {
service: 'clip',
operation: 'createPayment',
paymentId: response.data.id,
amount: params.amount,
tenantId,
});
return response.data;
} catch (error) {
const clipError = error.response?.data;
this.logger.error('Clip payment failed', {
service: 'clip',
operation: 'createPayment',
code: clipError?.code || error.response?.status,
message: clipError?.message || error.message,
tenantId,
});
switch (error.response?.status) {
case 400:
throw new BadRequestException(clipError?.message || 'Parametros de pago invalidos');
case 401:
throw new Error('Credenciales de Clip invalidas');
case 402:
throw new BadRequestException('Pago rechazado: ' + clipError?.decline_reason);
case 429:
// Encolar para reintento
await this.queue.add('clip-payment-retry', { params, tenantId });
throw new Error('Servicio ocupado, reintentando...');
default:
throw error;
}
}
}
}
```
---
## 7. Fallbacks
### Estrategia de Fallback
| Escenario | Fallback | Tiempo maximo |
|-----------|----------|---------------|
| Clip API caida | Encolar pago para reintento | 24 horas |
| Rate limited | Throttle + cola prioritaria | 1 hora |
| Pago rechazado | Ofrecer link de pago alternativo | Inmediato |
| Timeout | Verificar estado y reintentar | 3 intentos |
### Modo Degradado
Si Clip no esta disponible:
- Pagos presenciales se encolan en Redis para reintento
- Usuario puede optar por efectivo y registrar manualmente
- Links de pago existentes siguen funcionando
- Notificacion a admin sobre estado del servicio
```typescript
async createPaymentWithFallback(
params: CreatePaymentDto,
tenantId: string
): Promise<PaymentResult> {
try {
return await this.createPayment(params, tenantId);
} catch (error) {
if (this.isClipUnavailable(error)) {
// Encolar para reintento posterior
const jobId = await this.queue.add('clip-payment-pending', {
params,
tenantId,
createdAt: new Date().toISOString(),
});
return {
status: 'pending_retry',
jobId,
message: 'Pago encolado, se procesara cuando Clip este disponible',
};
}
throw error;
}
}
```
---
## 8. Multi-tenant
### Modelo de Credenciales
- [x] **Por Tenant:** Cada tenant puede configurar sus propias credenciales Clip
- [x] **Global con fallback:** Credenciales compartidas si tenant no tiene propias
### Almacenamiento
```sql
-- En schema payments
CREATE TABLE payments.tenant_clip_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES auth.tenants(id) NOT NULL,
api_key TEXT NOT NULL, -- Encriptado con AES-256
secret_key TEXT NOT NULL, -- Encriptado con AES-256
merchant_id VARCHAR(50) NOT NULL,
webhook_secret TEXT, -- Encriptado
environment VARCHAR(20) DEFAULT 'sandbox',
commission_rate DECIMAL(5,4) DEFAULT 0.0360, -- 3.6% default
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(tenant_id)
);
-- Indice para busqueda rapida
CREATE INDEX idx_tenant_clip_config_tenant ON payments.tenant_clip_config(tenant_id);
```
### Contexto en Llamadas
```typescript
@Injectable()
export class ClipService {
async getClientForTenant(tenantId: string): Promise<ClipClient> {
// Buscar configuracion del tenant
const config = await this.configRepo.findOne({ where: { tenantId } });
if (config?.is_active) {
return new ClipClient({
apiKey: this.decrypt(config.api_key),
secretKey: this.decrypt(config.secret_key),
merchantId: config.merchant_id,
environment: config.environment,
});
}
// Fallback a credenciales globales
return new ClipClient({
apiKey: this.configService.get('CLIP_API_KEY'),
secretKey: this.configService.get('CLIP_SECRET_KEY'),
merchantId: this.configService.get('CLIP_MERCHANT_ID'),
environment: this.configService.get('CLIP_ENVIRONMENT'),
});
}
async createPaymentForTenant(
tenantId: string,
params: CreatePaymentDto
): Promise<ClipPayment> {
const client = await this.getClientForTenant(tenantId);
return client.payments.create(params);
}
}
```
---
## 9. Webhooks
### Endpoints Registrados
| Evento | Endpoint Local | Descripcion |
|--------|----------------|-------------|
| `payment.created` | `/webhooks/clip` | Pago creado |
| `payment.approved` | `/webhooks/clip` | Pago aprobado |
| `payment.declined` | `/webhooks/clip` | Pago rechazado |
| `payment.refunded` | `/webhooks/clip` | Pago reembolsado |
| `payment_link.paid` | `/webhooks/clip` | Link de pago completado |
### Validacion HMAC de Firma
```typescript
import * as crypto from 'crypto';
function verifyClipWebhookSignature(
payload: string,
signature: string,
webhookSecret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(payload, 'utf8')
.digest('hex');
// Comparacion segura contra timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Controller
@Post('/webhooks/clip')
async handleClipWebhook(
@Body() body: any,
@Headers('X-Clip-Signature') signature: string,
@Req() request: Request
): Promise<{ received: boolean }> {
const rawBody = (request as any).rawBody;
// Determinar webhook secret (puede ser por tenant)
const webhookSecret = await this.getWebhookSecret(body.merchant_id);
if (!verifyClipWebhookSignature(rawBody, signature, webhookSecret)) {
throw new UnauthorizedException('Invalid webhook signature');
}
await this.processWebhookEvent(body);
return { received: true };
}
```
### Configuracion en Clip Dashboard
1. Acceder a Clip Dashboard > Desarrolladores > Webhooks
2. Agregar endpoint: `https://api.michangarrito.com/webhooks/clip`
3. Seleccionar eventos: `payment.*`, `payment_link.*`
4. Guardar y copiar el Webhook Secret
5. Configurar en variable de entorno `CLIP_WEBHOOK_SECRET`
---
## 10. Testing
### Modo Sandbox
| Ambiente | Credenciales | Comportamiento |
|----------|--------------|----------------|
| Sandbox | `clip_api_test_*` | Pagos simulados, sin cargos reales |
| Production | `clip_api_live_*` | Pagos reales con tarjetas |
### Tarjetas de Prueba Clip
```
# Pago exitoso
4242 4242 4242 4242 Exp: 12/28 CVV: 123
# Pago rechazado - Fondos insuficientes
4000 0000 0000 0002 Exp: 12/28 CVV: 123
# Pago rechazado - Tarjeta robada
4000 0000 0000 0069 Exp: 12/28 CVV: 123
# Requiere autenticacion 3D Secure
4000 0027 6000 3184 Exp: 12/28 CVV: 123
```
### Comandos de Test
```bash
# Test crear pago
curl -X POST https://api-sandbox.clip.mx/v1/payments \
-H "Authorization: Bearer ${CLIP_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"amount": 150.00,
"currency": "MXN",
"description": "Venta de prueba MiChangarrito",
"reference": "test-order-001"
}'
# Test consultar pago
curl -X GET "https://api-sandbox.clip.mx/v1/payments/${PAYMENT_ID}" \
-H "Authorization: Bearer ${CLIP_API_KEY}"
# Test crear link de pago
curl -X POST https://api-sandbox.clip.mx/v1/payment-links \
-H "Authorization: Bearer ${CLIP_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"amount": 299.00,
"currency": "MXN",
"description": "Pedido a domicilio",
"expires_at": "2026-01-20T23:59:59Z"
}'
# Test webhook local (simular evento)
curl -X POST http://localhost:3143/webhooks/clip \
-H "Content-Type: application/json" \
-H "X-Clip-Signature: $(echo -n '{"event":"payment.approved","data":{"id":"pay_123"}}' | openssl dgst -sha256 -hmac ${CLIP_WEBHOOK_SECRET})" \
-d '{"event":"payment.approved","data":{"id":"pay_123","amount":150.00,"status":"approved"}}'
```
---
## 11. Monitoreo
### Metricas a Monitorear
| Metrica | Descripcion | Alerta |
|---------|-------------|--------|
| Latencia API | Tiempo de respuesta Clip API | > 5s |
| Error Rate | % de requests fallidos | > 5% |
| Decline Rate | % de pagos rechazados | > 15% |
| Webhook Delay | Tiempo entre pago y webhook | > 30s |
| Success Rate | % de pagos exitosos | < 85% |
| Daily Volume | Volumen diario en MXN | Anomalia |
### Logs Estructurados
```typescript
// Pago exitoso
this.logger.info('Clip payment successful', {
service: 'clip',
operation: 'createPayment',
tenantId: context.tenantId,
paymentId: result.id,
amount: params.amount,
currency: 'MXN',
duration: durationMs,
});
// Pago rechazado
this.logger.warn('Clip payment declined', {
service: 'clip',
operation: 'createPayment',
tenantId: context.tenantId,
declineCode: error.decline_code,
declineReason: error.decline_reason,
amount: params.amount,
});
// Webhook recibido
this.logger.info('Clip webhook received', {
service: 'clip',
operation: 'webhook',
event: payload.event,
paymentId: payload.data.id,
merchantId: payload.merchant_id,
});
```
### Dashboard Recomendado
Configurar en Grafana/DataDog:
- Grafica de volumen de pagos por hora
- Grafica de tasa de exito/rechazo
- Alertas de latencia y errores
- Resumen de comisiones acumuladas
---
## 12. Referencias
### Documentacion Oficial Clip
- [Clip API Documentation](https://developer.clip.mx/docs)
- [Clip Dashboard](https://dashboard.clip.mx/)
- [Webhooks Reference](https://developer.clip.mx/docs/webhooks)
- [Tarjetas de Prueba](https://developer.clip.mx/docs/testing)
### Informacion de Clip Mexico
- Comision estandar: 3.6% + IVA por transaccion
- Depositos: 24-48 horas habiles
- Soporte: soporte@clip.mx
- Telefono: 55 4162 5252
### Modulos Relacionados
- [Payments Module](../../apps/backend/src/modules/payments/)
- [Sales Module](../../apps/backend/src/modules/sales/)
- [Arquitectura Multi-Tenant](../90-transversal/ARQUITECTURA-MULTI-TENANT-INTEGRACIONES.md)
### Integraciones Relacionadas
- [INT-002: Stripe](./INT-002-stripe.md) - Suscripciones de MiChangarrito
- [INT-004: MercadoPago](./INT-004-mercadopago.md) - Alternativa de pagos
- [INT-006: CoDi Banxico](./INT-006-codi-banxico.md) - Pagos QR bancarios
---
## 13. Notas de Implementacion
- La integracion requiere cuenta de desarrollador en Clip Portal
- Clip cobra comision del 3.6% + IVA por transaccion
- Los fondos se depositan en 24-48 horas habiles
- Implementar idempotency keys para evitar cobros duplicados
- Validar firma HMAC en todos los webhooks recibidos
- Manejar los estados de pago: pending, approved, declined, refunded
- Considerar limites de rate limiting de la API (100 req/min)
- En ambiente sandbox usar tarjetas de prueba proporcionadas por Clip
---
**Ultima actualizacion:** 2026-01-17
**Autor:** Backend Team