Migración de Pasarela de Pagos: SOAP a REST

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

  1. Timeouts frecuentes: SOAP es verboso (XML pesado), causando latencia
  2. Manejo de errores deficiente: No se retry automático, estados ambiguos
  3. Sin logs estructurados: Debugging era imposible
  4. Código acoplado: Lógica de negocio mezclada con infraestructura
  5. 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.

Artículos relacionados