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

18 KiB

id type title provider status integration_type created_at updated_at simco_version tags
INT-005 Integration Terminal de pagos Clip Clip México Mock Pagos con tarjeta 2026-01-10 2026-01-17 4.0.1
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

# 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
  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

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

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
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

  • Por Tenant: Cada tenant puede configurar sus propias credenciales Clip
  • Global con fallback: Credenciales compartidas si tenant no tiene propias

Almacenamiento

-- 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

@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

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

# 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

// 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

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

Integraciones Relacionadas


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