Migración de Pasarela de Pagos: SOAP a REST
El Problema: SOAP Legacy y Baja Tasa de Aprobación
Cuando audité el sistema de pagos en Grupo Antyr, encontré un problema crítico: solo el 65% de las transacciones eran aprobadas. Esto significaba que de cada 100 clientes intentando pagar, 35 fallaban y generaban tickets de soporte.
El sistema legacy usaba SOAP (protocolo de los 2000s) para comunicarse con MercadoPago, y tenía varios problemas:
Problemas Identificados
- Timeouts frecuentes: SOAP es verboso (XML pesado), causando latencia
- Manejo de errores deficiente: No se retry automático, estados ambiguos
- Sin logs estructurados: Debugging era imposible
- Código acoplado: Lógica de negocio mezclada con infraestructura
- Sin validaciones modernas: Campos opcionales mal manejados
Impacto en el negocio:
- 💰 Pérdida de ventas (35% de transacciones fallidas)
- 🎫 80% de tickets de soporte relacionados a pagos
- 😤 Frustración de usuarios
- ⏱️ Horas de desarrollo perdidas en debugging
La Solución: Migración a REST con SDK Oficial
Diseñé e implementé una migración completa del sistema a la API REST moderna de MercadoPago, utilizando el SDK oficial de MercadoPago integrado con Laravel. Esta decisión nos dio:
- ✅ Abstracción robusta sobre la API REST
- ✅ Manejo de errores mejorado out-of-the-box
- ✅ Tipos de datos validados por el SDK
- ✅ Actualizaciones automáticas cuando MercadoPago cambia su API
Arquitectura de la Solución
1. Service Layer con SDK de MercadoPago
Implementé un service robusto usando el SDK oficial con reintentos automáticos:
use MercadoPago\Client\Payment\PaymentClient;
use MercadoPago\MercadoPagoConfig;
class MercadoPagoService
{
private PaymentClient $client;
public function __construct()
{
MercadoPagoConfig::setAccessToken(config('services.mercadopago.access_token'));
$this->client = new PaymentClient();
}
public function createPayment(PaymentDTO $dto): PaymentResult
{
return $this->withRetry(function() use ($dto) {
$payment = $this->client->create([
'transaction_amount' => $dto->amount,
'payment_method_id' => $dto->paymentMethod,
'payer' => [
'email' => $dto->payer->email,
'identification' => [
'type' => $dto->payer->identificationType,
'number' => $dto->payer->identificationNumber,
],
],
'description' => $dto->description,
'external_reference' => $dto->externalReference,
]);
return PaymentResult::fromSdk($payment);
}, maxRetries: 3);
}
}
2. DTOs para Validación
Creé Data Transfer Objects que validan antes de enviar:
class PaymentDTO
{
public function __construct(
public float $amount,
public string $paymentMethod,
public PayerDTO $payer,
public string $description,
public ?string $externalReference = null,
) {
$this->validate();
}
private function validate(): void
{
if ($this->amount <= 0) {
throw new InvalidPaymentException('Amount must be positive');
}
// Más validaciones...
}
}
3. Logging Estructurado
Implementé logging completo con contexto para debugging:
Log::info('Payment initiated', [
'amount' => $payment->amount,
'method' => $payment->paymentMethod,
'external_ref' => $payment->externalReference,
]);
4. Manejo de Webhooks
MercadoPago envía webhooks para cambios de estado. Implementé un handler robusto:
class MercadoPagoWebhookController
{
public function handle(Request $request)
{
// Validar firma
if (!$this->validateSignature($request)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
// Procesar de forma idempotente
$this->paymentService->processWebhook($request->all());
return response()->json(['status' => 'ok']);
}
}
5. Estados de Transacción Claros
Creé un enum para estados claros:
enum PaymentStatus: string
{
case PENDING = 'pending';
case APPROVED = 'approved';
case REJECTED = 'rejected';
case CANCELLED = 'cancelled';
case REFUNDED = 'refunded';
case IN_PROCESS = 'in_process';
}
Resultados: De 65% a 95% de Aprobación
Los números hablan por sí solos:
✅ Tasa de aprobación: 65% → 95% (+30 puntos porcentuales)
✅ Tickets de soporte: Reducción del 80%
✅ Tiempo de respuesta: 3-5s → <1s promedio
✅ Timeouts: De 20-30 diarios → 0-2 mensuales
✅ Debugging: De horas → minutos (gracias a logs estructurados)
Impacto en el Negocio
- 💰 +30% más transacciones exitosas = más ingresos
- 🎫 -80% tickets = equipo de soporte más enfocado
- 😊 Usuarios más satisfechos = mejor reputación
- 👨💻 Developers más productivos = menos firefighting
Aprendizajes Clave
1. SOAP está Muerto (y con Razón)
SOAP fue genial en 2000, pero en 2024:
- ❌ XML es pesado y difícil de debuggear
- ❌ Overhead de procesamiento innecesario
- ❌ Tooling limitado y anticuado
REST/JSON es:
- ✅ Ligero y legible
- ✅ Fácil de debuggear (cualquier dev tools lo lee)
- ✅ Tooling moderno (Postman, Insomnia, etc.)
2. Usa el SDK Oficial, No Reinventes la Rueda
Al principio consideré hacer llamadas HTTP directas con Guzzle/Laravel HTTP, pero el SDK oficial de MercadoPago nos ahorró semanas de desarrollo:
Ventajas del SDK:
- ✅ Tipos validados: El SDK valida los datos antes de enviar
- ✅ Manejo de errores robusto: Excepciones tipadas (
MPException,MPApiException) - ✅ Actualizaciones automáticas: Cuando MercadoPago cambia su API, solo actualizas el SDK
- ✅ Retry logic incluido: El SDK maneja reintentos de forma inteligente
- ✅ Menos código: Lo que serían 50 líneas de HTTP son 10 con el SDK
Ejemplo de error handling con SDK:
try {
$payment = $this->client->create($data);
} catch (MPApiException $e) {
// Error de la API (400, 401, etc.)
Log::error('MercadoPago API error', [
'status' => $e->getApiResponse()->getStatusCode(),
'content' => $e->getApiResponse()->getContent(),
]);
} catch (MPException $e) {
// Error del SDK (network, config, etc.)
Log::error('MercadoPago SDK error', ['message' => $e->getMessage()]);
}
3. Retry Logic es Crítico
Los pagos son operaciones sensibles a la red. Un retry bien implementado:
- Resuelve 90% de errores transitorios
- Exponential backoff evita saturar el servidor
- Idempotencia previene duplicados
4. DTOs Previenen Bugs
Validar data antes de enviar previene:
- Errores en producción
- Roundtrips innecesarios al API
- Debugging tedioso
5. Logs Estructurados Son Oro
Cuando algo falla (y fallará), logs estructurados con contexto son la diferencia entre:
- “No sé qué pasó” vs “Sé exactamente qué pasó”
6. Webhooks > Polling
No preguntes “¿ya se procesó?”. Deja que MercadoPago te avise:
- Menor latencia
- Menos carga en tu servidor
- Actualizaciones en tiempo real
Stack Técnico
- Backend: Laravel 10 (PHP 8.2)
- SDK: MercadoPago SDK for PHP v3.0 (
mercadopago/dx-php) - API: MercadoPago REST API v1 (vía SDK)
- Validación: Custom DTOs
- Logging: Laravel Log (Monolog)
- Queue: Redis + Horizon (para procesamiento asíncrono)
- Testing: PHPUnit + Mockery
Instalación y Configuración
1. Instalación del SDK:
composer require mercadopago/dx-php
2. Configuración en Laravel:
// config/services.php
'mercadopago' => [
'access_token' => env('MERCADOPAGO_ACCESS_TOKEN'),
'public_key' => env('MERCADOPAGO_PUBLIC_KEY'),
'webhook_secret' => env('MERCADOPAGO_WEBHOOK_SECRET'),
],
// .env
MERCADOPAGO_ACCESS_TOKEN=APP_USR-xxx
MERCADOPAGO_PUBLIC_KEY=APP_USR-xxx
MERCADOPAGO_WEBHOOK_SECRET=xxx
3. Service Provider (opcional):
// app/Providers/AppServiceProvider.php
public function boot()
{
MercadoPagoConfig::setAccessToken(
config('services.mercadopago.access_token')
);
}
Conclusión
Migrar de SOAP a REST no fue solo un upgrade técnico, fue una transformación del negocio. La inversión de 3 semanas de desarrollo resultó en:
- 30% más transacciones exitosas
- 80% menos soporte
- Base de código mantenible y testeable
- Equipo más feliz y productivo
Si tienes un sistema legacy de pagos, no esperes más. Los beneficios son inmediatos y medibles.
Checklist para Tu Propia Migración
- Audita tu sistema actual (tasa de éxito, errores comunes)
- Investiga el API REST de tu proveedor
- Diseña DTOs y validaciones
- Implementa retry logic con exponential backoff
- Agrega logging estructurado
- Implementa webhooks (no polling)
- Testing exhaustivo con sandbox
- Deploy gradual (feature flags)
- Monitoreo continuo
¿Estás enfrentando problemas similares? Comparte tu experiencia en los comentarios o contáctame en LinkedIn.