| id |
type |
title |
provider |
status |
integration_type |
created_at |
updated_at |
simco_version |
tags |
| INT-004 |
Integration |
Mercado Pago |
MercadoPago |
Pendiente |
payments |
2026-01-04 |
2026-01-17 |
4.0.1 |
| mercadopago |
| payments |
| point-of-sale |
| multi-tenant |
|
INT-004: Mercado Pago
Metadata
| Campo |
Valor |
| Codigo |
INT-004 |
| Proveedor |
Mercado Libre |
| Tipo |
Terminal de Pago |
| Estado |
Pendiente |
| Multi-tenant |
Si (por tenant) |
| Fecha integracion |
- |
| Ultimo update |
2026-01-17 |
| Owner |
Backend Team |
1. Descripcion
Mercado Pago es una opcion de terminal de pago fisica para que los duenos de tiendas puedan aceptar pagos con tarjeta. A diferencia de Stripe (que maneja suscripciones), MercadoPago se usa para cobros en punto de venta.
Casos de uso principales:
- Cobro con tarjeta en tienda fisica
- QR para cobro
- Generacion de links de pago
- Integracion con terminal Point
2. Credenciales Requeridas
Variables de Entorno
| Variable |
Descripcion |
Tipo |
Obligatorio |
MERCADOPAGO_ACCESS_TOKEN |
Access Token de la aplicacion |
string |
SI |
MERCADOPAGO_PUBLIC_KEY |
Public Key para frontend |
string |
SI |
MERCADOPAGO_WEBHOOK_SECRET |
Secret para webhooks (IPN) |
string |
SI |
Ejemplo de .env
# Mercado Pago
MERCADOPAGO_ACCESS_TOKEN=APP_USR-xxxxxxxx
MERCADOPAGO_PUBLIC_KEY=APP_USR-xxxxxxxx
MERCADOPAGO_WEBHOOK_SECRET=xxxxxxxx
Obtencion de Credenciales
- Crear cuenta en Mercado Pago Developers
- Crear aplicacion
- Obtener credenciales de produccion
- Configurar URL de IPN (webhook)
3. Endpoints/SDK Utilizados
Operaciones Planificadas
| Operacion |
SDK Method |
Endpoint |
Descripcion |
| Crear Preferencia |
preference.create() |
POST /checkout/preferences |
Link de pago |
| Crear QR |
qr.create() |
POST /instore/qr/seller |
Cobro por QR |
| Consultar Pago |
payment.get() |
GET /v1/payments/{id} |
Estado del pago |
| Reembolso |
payment.refund() |
POST /v1/payments/{id}/refunds |
Devolucion |
| Buscar Pagos |
payment.search() |
GET /v1/payments/search |
Historial de pagos |
SDK Planificado
import { MercadoPagoConfig, Preference, Payment } from 'mercadopago';
const client = new MercadoPagoConfig({
accessToken: process.env.MERCADOPAGO_ACCESS_TOKEN,
});
const preference = new Preference(client);
// Crear link de pago
const result = await preference.create({
body: {
items: [{
title: 'Venta en tienda',
unit_price: 100,
quantity: 1,
currency_id: 'MXN',
}],
back_urls: {
success: 'https://michangarrito.com/pago/exitoso',
failure: 'https://michangarrito.com/pago/fallido',
pending: 'https://michangarrito.com/pago/pendiente',
},
auto_return: 'approved',
external_reference: 'sale_12345',
},
});
4. Rate Limits
| Limite |
Valor |
Periodo |
Accion si excede |
| API calls (general) |
10,000 |
por minuto |
429 Too Many Requests |
| Crear preferencias |
1,000 |
por minuto |
429 |
| Consultar pagos |
5,000 |
por minuto |
429 |
| Webhooks entrantes |
Sin limite |
- |
N/A |
Estrategia de Retry
const delays = [1000, 2000, 4000, 8000];
async function callWithRetry<T>(fn: () => Promise<T>): Promise<T> {
for (let attempt = 0; attempt < delays.length; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429) {
await sleep(delays[attempt]);
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
// Uso
const payment = await callWithRetry(() => paymentService.get(paymentId));
5. Manejo de Errores
Codigos de Error
| Codigo |
Descripcion |
Accion Recomendada |
Retry |
| 400 |
Parametros invalidos |
Validar payload antes de enviar |
NO |
| 401 |
Token invalido o expirado |
Regenerar access token |
NO |
| 402 |
Pago rechazado |
Informar al usuario, sugerir otro metodo |
NO |
| 403 |
Cuenta no autorizada |
Verificar permisos de la aplicacion |
NO |
| 404 |
Recurso no encontrado |
Verificar ID de pago/preferencia |
NO |
| 429 |
Rate limit excedido |
Backoff exponencial |
SI |
| 500 |
Error interno MercadoPago |
Retry con backoff |
SI |
| 503 |
Servicio no disponible |
Retry con backoff, activar fallback |
SI |
Ejemplo de Manejo
import { Logger } from '@nestjs/common';
try {
const payment = await mercadopagoService.createPayment(paymentData);
return { success: true, paymentId: payment.id };
} catch (error) {
const logger = new Logger('MercadoPago');
logger.error('MercadoPago error', {
service: 'mercadopago',
operation: 'createPayment',
status: error.status,
code: error.cause?.code,
message: error.message,
tenantId: context.tenantId,
externalReference: paymentData.external_reference,
});
// Clasificar error
if (error.status === 402) {
return { success: false, error: 'payment_rejected', userMessage: 'El pago fue rechazado' };
}
if (error.status === 429 || error.status >= 500) {
// Encolar para reintento
await this.queue.add('mercadopago-retry', { paymentData, tenantId: context.tenantId });
return { success: false, error: 'queued', userMessage: 'Procesando pago, te notificaremos' };
}
throw error;
}
6. Fallbacks
Estrategia de Fallback
| Escenario |
Fallback |
Tiempo maximo |
| API caida |
Cola Redis para reintentar |
24 horas |
| Rate limited |
Throttle + prioridad |
1 hora |
| Token expirado |
Alerta admin + regenerar |
Inmediato |
| Pago rechazado |
Sugerir efectivo o transferencia |
N/A |
Modo Degradado
Si MercadoPago no esta disponible:
async processPayment(saleId: string, amount: number, method: string) {
if (await this.mercadopagoHealthCheck()) {
return this.mercadopagoService.createPayment({ saleId, amount });
}
// Modo degradado: registrar pago pendiente
this.logger.warn('MercadoPago unavailable, entering degraded mode', {
service: 'mercadopago',
saleId,
});
// Opciones alternativas
return {
status: 'degraded',
alternatives: [
{ method: 'cash', message: 'Aceptar pago en efectivo' },
{ method: 'bank_transfer', message: 'Solicitar transferencia bancaria' },
],
pendingQueue: await this.queue.add('payment-pending', { saleId, amount }),
};
}
Recuperacion Automatica
- Worker de Redis revisa cada 5 minutos si API esta disponible
- Reintentos automaticos de pagos encolados
- Notificacion al tenant cuando pago se procesa
7. Multi-tenant
Modelo de Credenciales
Los fondos van directo a la cuenta del dueno de la tienda.
Almacenamiento
CREATE TABLE sales.tenant_mercadopago_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES auth.tenants(id) NOT NULL,
access_token TEXT NOT NULL, -- Encriptado con AES-256
public_key VARCHAR(100) NOT NULL,
collector_id VARCHAR(50),
user_id VARCHAR(50),
is_sandbox BOOLEAN DEFAULT false,
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 busquedas
CREATE INDEX idx_mercadopago_config_tenant ON sales.tenant_mercadopago_config(tenant_id);
Contexto en Llamadas
async createPaymentForTenant(
tenantId: UUID,
saleId: string,
amount: number,
description: string
): Promise<PaymentResult> {
const config = await this.getTenantMercadopagoConfig(tenantId);
if (!config || !config.is_active) {
throw new MercadoPagoNotConfiguredError(tenantId);
}
// Crear cliente con credenciales del tenant
const client = new MercadoPagoConfig({
accessToken: this.decrypt(config.access_token),
});
const preference = new Preference(client);
return preference.create({
body: {
items: [{
title: description,
unit_price: amount,
quantity: 1,
currency_id: 'MXN',
}],
external_reference: saleId,
notification_url: `https://api.michangarrito.com/webhooks/mercadopago/${tenantId}`,
},
});
}
8. Webhooks (IPN)
Endpoints Registrados
| Evento |
Endpoint Local |
Descripcion |
payment |
/webhooks/mercadopago/:tenantId |
Pago recibido/actualizado |
merchant_order |
/webhooks/mercadopago/:tenantId |
Orden actualizada |
point_integration_ipn |
/webhooks/mercadopago/:tenantId |
Terminal Point |
Validacion de Firma IPN
import * as crypto from 'crypto';
function verifyMercadoPagoSignature(
xSignature: string,
xRequestId: string,
dataId: string,
webhookSecret: string
): boolean {
// Extraer ts y hash del header x-signature
// Formato: ts=xxx,v1=hash
const parts = xSignature.split(',');
const ts = parts.find(p => p.startsWith('ts='))?.split('=')[1];
const hash = parts.find(p => p.startsWith('v1='))?.split('=')[1];
if (!ts || !hash) {
return false;
}
// Construir el manifest para validar
const manifest = `id:${dataId};request-id:${xRequestId};ts:${ts};`;
// Calcular HMAC
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(manifest)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(expected));
}
// Uso en controller
@Post('webhooks/mercadopago/:tenantId')
async handleWebhook(
@Param('tenantId') tenantId: string,
@Headers('x-signature') signature: string,
@Headers('x-request-id') requestId: string,
@Query('data.id') dataId: string,
@Body() body: MercadoPagoWebhookPayload,
) {
const config = await this.getTenantConfig(tenantId);
if (!verifyMercadoPagoSignature(signature, requestId, dataId, config.webhook_secret)) {
throw new UnauthorizedException('Invalid webhook signature');
}
// Procesar el evento
await this.processWebhookEvent(tenantId, body);
return { received: true };
}
Configuracion en MercadoPago
- MercadoPago Dashboard → Tu aplicacion → Webhooks
- URL de notificacion:
https://api.michangarrito.com/webhooks/mercadopago/{tenant_id}
- Eventos a suscribir:
payment, merchant_order
- Guardar y probar
9. Testing
Modo Sandbox/Test
| Ambiente |
Credenciales |
Datos |
| Sandbox |
Test Access Token |
Tarjetas de prueba MercadoPago |
| Production |
Production Access Token |
Tarjetas reales |
Tarjetas de Prueba (Mexico)
| Numero |
CVV |
Vencimiento |
Resultado |
| 5474 9254 3267 0366 |
123 |
11/25 |
Aprobado |
| 4509 9535 6623 3704 |
123 |
11/25 |
Rechazado por fondos |
| 3711 803032 57522 |
1234 |
11/25 |
Pendiente |
Comandos de Test
# Test de creacion de preferencia
curl -X POST https://api.mercadopago.com/checkout/preferences \
-H "Authorization: Bearer ${MERCADOPAGO_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"items": [{
"title": "Test Product",
"quantity": 1,
"unit_price": 100,
"currency_id": "MXN"
}],
"external_reference": "test_001"
}'
# Consultar estado de pago
curl -X GET "https://api.mercadopago.com/v1/payments/${PAYMENT_ID}" \
-H "Authorization: Bearer ${MERCADOPAGO_ACCESS_TOKEN}"
# Test de webhook local
curl -X POST http://localhost:3143/webhooks/mercadopago/test-tenant \
-H "Content-Type: application/json" \
-H "x-signature: ts=1234567890,v1=test" \
-H "x-request-id: test-request-id" \
-d '{"action":"payment.created","data":{"id":"12345678"}}'
Tests Unitarios
describe('MercadoPagoService', () => {
it('should create preference successfully', async () => {
const mockPreference = { id: 'pref_123', init_point: 'https://...' };
jest.spyOn(preferenceClient, 'create').mockResolvedValue(mockPreference);
const result = await service.createPaymentLink({
saleId: 'sale_001',
amount: 100,
description: 'Test sale',
});
expect(result.id).toBe('pref_123');
expect(result.init_point).toBeDefined();
});
it('should handle rate limit with retry', async () => {
jest.spyOn(paymentClient, 'get')
.mockRejectedValueOnce({ status: 429 })
.mockResolvedValueOnce({ id: 'pay_123', status: 'approved' });
const result = await service.getPaymentWithRetry('pay_123');
expect(result.status).toBe('approved');
});
});
10. Monitoreo
Metricas a Monitorear
| Metrica |
Descripcion |
Alerta |
| Latencia |
Tiempo de respuesta API |
> 5s |
| Error Rate |
% de requests fallidos |
> 5% |
| Payments Created |
Pagos creados por hora |
< 1 (posible caida) |
| Payment Success Rate |
% de pagos aprobados |
< 70% |
| Webhook Delay |
Tiempo entre evento y procesamiento |
> 30s |
| Queue Depth |
Pagos pendientes en cola |
> 100 |
Logs Estructurados
// Pago creado exitosamente
this.logger.info('MercadoPago payment created', {
service: 'mercadopago',
operation: 'createPayment',
tenantId: context.tenantId,
preferenceId: result.id,
externalReference: saleId,
amount: amount,
currency: 'MXN',
duration: durationMs,
});
// Webhook recibido
this.logger.info('MercadoPago webhook received', {
service: 'mercadopago',
operation: 'webhook',
tenantId: tenantId,
action: body.action,
paymentId: body.data?.id,
type: body.type,
});
// Error en pago
this.logger.error('MercadoPago payment failed', {
service: 'mercadopago',
operation: 'createPayment',
tenantId: context.tenantId,
errorCode: error.cause?.code,
errorMessage: error.message,
saleId: saleId,
});
Dashboard Sugerido
# Grafana dashboard panels
panels:
- title: "Pagos por Estado"
type: pie_chart
query: "mercadopago_payments_total BY status"
- title: "Latencia API"
type: time_series
query: "histogram_quantile(0.95, mercadopago_api_duration_seconds)"
- title: "Tasa de Errores"
type: stat
query: "rate(mercadopago_errors_total[5m]) / rate(mercadopago_requests_total[5m])"
11. Referencias
Documentacion Oficial
Modulos Relacionados
Soporte
Estado de Implementacion
Checklist
Bloqueadores
- Requiere onboarding de tenants a MercadoPago
- Proceso de verificacion de cuenta
Ultima actualizacion: 2026-01-17
Estado: PENDIENTE DE IMPLEMENTACION