Skip to main content

Errores Comunes

Esta guía te ayudará a resolver los problemas más frecuentes al integrar con SUNAT.

Errores de Conexión

Error: No se pudo conectar con SUNAT

Síntomas:
  • Timeout al enviar comprobantes
  • Error de conexión SSL/TLS
  • No hay respuesta del servidor
Soluciones:
  1. Verificar conectividad:
    # Probar conexión con el endpoint
    curl -I https://e-factura.sunat.gob.pe/ol-ti-itcpfegem/billService
    
  2. Verificar certificados SSL del servidor:
    # Actualizar certificados CA
    sudo update-ca-certificates
    
  3. Verificar firewall:
    • Permitir conexiones salientes al puerto 443 (HTTPS)
    • Permitir conexiones a dominios *.sunat.gob.pe
  4. Verificar extensión PHP:
    # Verificar que SOAP esté habilitado
    php -m | grep soap
    php -m | grep openssl
    
Los servicios de SUNAT pueden tener mantenimientos programados. Verifica el estado en la página oficial de SUNAT.

Error: SSL certificate problem

Mensaje:
SSL certificate problem: unable to get local issuer certificate
Solución:
  1. Actualizar el archivo cacert.pem en tu instalación de PHP:
    # Descargar certificados actualizados
    wget https://curl.se/ca/cacert.pem
    
    # Mover a la ubicación de PHP
    sudo mv cacert.pem /etc/ssl/certs/
    
  2. Configurar php.ini:
    openssl.cafile=/etc/ssl/certs/cacert.pem
    curl.cainfo=/etc/ssl/certs/cacert.pem
    

Errores de Certificado Digital

Error: No se encontró certificado PEM

Mensaje:
RuntimeException: No se encontró certificado PEM para la empresa {RUC}
Ubicación esperada:
storage/app/sunat/certificados/{RUC}-cert.pem
Solución:
  1. Verificar que el certificado exista:
    ls -la storage/app/sunat/certificados/
    
  2. Verificar permisos del archivo:
    chmod 600 storage/app/sunat/certificados/*.pem
    
  3. Si tienes un certificado .pfx, convertirlo a .pem:
    openssl pkcs12 -in certificado.pfx -out {RUC}-cert.pem -nodes
    

Error: El certificado digital ha expirado

Código SUNAT: 1033 Solución:
  • Renovar el certificado digital con tu proveedor (PSE)
  • Actualizar el archivo .pem con el nuevo certificado
  • Verificar fecha de expiración:
    openssl x509 -in certificado.pem -noout -enddate
    

Error: El RUC del certificado no coincide

Código SUNAT: 2324 Mensaje:
El número de RUC del archivo no corresponde con el RUC del certificado digital
Causas:
  • El certificado digital está a nombre de un RUC diferente
  • Estás usando el certificado de prueba en producción
  • El modo de la empresa no coincide (beta vs production)
Solución:
  1. Verificar que el RUC en el certificado coincida con el de la empresa:
    openssl x509 -in certificado.pem -noout -subject
    
  2. Verificar el modo de la empresa:
    // Si modo = 'beta', usa RUC 20000000001
    // Si modo = 'production', usa el RUC real de la empresa
    echo $empresa->modo;
    echo $empresa->ruc;
    

Errores de Credenciales

Error: Credenciales SOL incorrectas

Código SUNAT: 1033, 0151 Mensaje:
El Usuario ingresado no existe o es inválido
La Clave ingresada es incorrecta
Solución:
  1. Verificar formato del usuario SOL:
    // El sistema concatena automáticamente RUC + usuario
    // Formato correcto en base de datos:
    $empresa->user_sol = 'ADMIN01';  // SIN el RUC
    
    // El servicio construye: {RUC}{user_sol}
    // Resultado: 20612706702ADMIN01
    
  2. Verificar credenciales en SUNAT:
    • Ingresa a SUNAT Operaciones en Línea
    • Verifica o regenera tu Clave SOL
  3. En ambiente beta, usar las credenciales de prueba:
    // Configuradas en config/sunat.php
    'beta' => [
        'ruc' => '20000000001',
        'usuario_sol' => 'MODDATOS',
        'clave_sol' => 'moddatos',
    ]
    

Error: Token GRE inválido o expirado

Para guías de remisión electrónicas (GRE API): Solución:
  1. Verificar credenciales OAuth en .env:
    SUNAT_GRE_CLIENT_ID=tu_client_id
    SUNAT_GRE_CLIENT_SECRET=tu_client_secret
    
  2. Verificar que las credenciales sean para el RUC correcto
  3. El token se obtiene automáticamente en cada request, no es necesario guardarlo

Errores de Validación de Documentos

Error: El comprobante ya existe

Código SUNAT: 2283, 1032 Mensaje:
El comprobante electrónico ya fue informado anteriormente
Causa:
  • Ya enviaste un comprobante con la misma serie y número
Solución:
  • No reenvíes comprobantes ya aceptados por SUNAT
  • Verifica el correlativo antes de generar un nuevo comprobante
  • Si necesitas corregir, usa una nota de crédito

Error: Serie o número inválido

Código SUNAT: 2108, 2109 Mensajes:
La serie no corresponde al tipo de comprobante
El número no puede ser menor al último número enviado
Formato correcto de series:
  • Facturas: F001, F002, etc.
  • Boletas: B001, B002, etc.
  • Notas de crédito factura: FC01, FC02
  • Notas de crédito boleta: BC01, BC02
  • Guías de remisión: T001, T002, V001
Solución:
// Formato de serie: 4 caracteres
$venta->serie = 'F001';  // Correcto
$venta->serie = 'F1';    // Incorrecto (muy corto)

// Número: secuencial, sin saltos
$ultimoNumero = Venta::where('serie', 'F001')
    ->max('numero');
$venta->numero = $ultimoNumero + 1;

Error: Datos del cliente inválidos

Código SUNAT: 2017, 2800, 2801 Mensajes:
El RUC del cliente no existe o no está activo
El DNI del cliente no es válido
Solución:
  1. Validar tipo de documento:
    // RUC: 11 dígitos, tipo doc = '6'
    if (strlen($cliente->documento) === 11) {
        $tipoDoc = '6';
    }
    // DNI: 8 dígitos, tipo doc = '1'
    elseif (strlen($cliente->documento) === 8) {
        $tipoDoc = '1';
    }
    
  2. Para facturas (tipo 01), el cliente DEBE tener RUC válido
  3. Para boletas (tipo 03), puede ser DNI o RUC

Error: Monto total no coincide

Código SUNAT: 2802, 3035 Mensaje:
Error en el cálculo del IGV
La suma de los totales no coincide con el total del comprobante
Solución: Verificar cálculos de IGV:
$igvRate = 0.18; // 18%
$total = 118.00;

// Cálculo correcto:
$montoGravada = round($total / ($igvRate + 1), 2); // 100.00
$igvMonto = round($montoGravada * $igvRate, 2);    // 18.00

// Verificar: $montoGravada + $igvMonto = $total
// 100.00 + 18.00 = 118.00 ✓
El servicio SunatService maneja estos cálculos automáticamente, pero verifica que los totales en la venta sean correctos.

Códigos de Error CDR (Constancia de Recepción)

Códigos de Aceptación

CódigoDescripciónAcción
0AceptadoComprobante válido y aceptado
0100Aceptado con observacionesRevisar observaciones pero es válido

Códigos de Rechazo Comunes

CódigoDescripciónSolución
1033Credenciales SOL incorrectasVerificar usuario y clave SOL
2017Cliente con RUC no válidoVerificar RUC del cliente en SUNAT
2108Serie no corresponde al tipoUsar formato correcto de serie
2283Comprobante ya existeNo reenviar, usar nota de crédito
2324RUC del certificado no coincideUsar certificado del RUC correcto
2335Fecha de emisión inválidaNo emitir documentos con fecha futura
2802Error en cálculo de IGVVerificar montos y redondeos
3035Suma de totales incorrectaVerificar cálculo de subtotal + IGV

Códigos de Proceso

CódigoDescripciónAcción
98En procesoEsperar y consultar nuevamente
99Error temporalReintentar más tarde

Errores de Guías de Remisión (GRE)

Error: Ticket en proceso (código 98)

Mensaje:
['codigo' => '98', 'en_proceso' => true]
Solución:
  • Es normal, el procesamiento puede tardar unos segundos
  • Consultar nuevamente después de 2-5 segundos:
$maxIntentos = 10;
$intento = 0;

do {
    sleep(2);
    $estado = $sunatService->consultarTicketGuia($guia);
    $intento++;
} while (
    isset($estado['en_proceso']) && 
    $estado['en_proceso'] && 
    $intento < $maxIntentos
);

Error: Datos del transportista inválidos

Código: 3001, 3002 Solución:
  1. Verificar datos según modalidad de transporte:
// Modalidad 01: Transporte público
if ($guia->mod_transporte === '01') {
    // Requiere datos del transportista:
    $guia->transportista_tipo_doc = '6';
    $guia->transportista_documento = '20123456789';
    $guia->transportista_nombre = 'TRANSPORTES SAC';
    $guia->transportista_nro_mtc = 'MTC123456';
}

// Modalidad 02: Transporte privado
if ($guia->mod_transporte === '02') {
    // Requiere datos del conductor y vehículo:
    $guia->conductor_tipo_doc = '1';
    $guia->conductor_documento = '12345678';
    $guia->conductor_nombres = 'JUAN';
    $guia->conductor_apellidos = 'PEREZ LOPEZ';
    $guia->conductor_licencia = 'Q12345678';
    $guia->vehiculo_placa = 'ABC-123';
}

Error: Peso total no puede ser cero

Solución:
$guia->peso_total = 15.5; // Peso en kg
$guia->und_peso_total = 'KGM'; // Kilogramos

Errores de Resumen Diario y Comunicación de Baja

Error: Ya existe un resumen para esta fecha

Solución:
  • Cada correlativo de resumen debe ser único por día
  • Incrementar el correlativo:
$ultimoCorrelativo = ResumenDiario::whereDate('fecha', today())
    ->max('correlativo');
    
$nuevoCorrelativo = str_pad($ultimoCorrelativo + 1, 3, '0', STR_PAD_LEFT);
// Resultado: '001', '002', '003', etc.

Error: Ticket no encontrado o expirado

Solución:
  • Los tickets expiran después de cierto tiempo
  • Consultar el ticket antes de medianoche
  • Guardar el ticket en la base de datos para consultarlo después

Cómo Leer las Respuestas de SUNAT

Respuesta Exitosa (CDR)

Cuando SUNAT acepta un comprobante:
$respuesta = $sunatService->enviarComprobante($venta);

// Estructura de respuesta exitosa:
[
    'success' => true,
    'codigo' => '0', // 0 = Aceptado
    'mensaje' => 'La Factura numero F001-00000123 ha sido aceptada',
    'cdr_url' => 'sunat/cdr/20612706702/R-20612706702-01-F001-00000123.zip'
]
El CDR (Constancia de Recepción) contiene:
  • Código de respuesta
  • Mensaje descriptivo
  • Firma digital de SUNAT
  • Observaciones (si las hay)

Respuesta con Error

$respuesta = $sunatService->enviarComprobante($venta);

// Estructura de respuesta con error:
[
    'success' => false,
    'codigo' => '2324',
    'message' => 'El número de RUC del archivo no corresponde con el RUC del certificado digital'
]

Respuesta con Observaciones

Algunos comprobantes son aceptados pero con observaciones:
// Código '0100' o similar
[
    'success' => true,
    'codigo' => '0100',
    'mensaje' => 'La Factura ha sido aceptada',
    'notas' => [
        'Observación: El producto X no tiene código interno'
    ]
]
Las observaciones no invalidan el comprobante, pero es recomendable corregirlas para futuros documentos.

Logs y Debugging

Revisar Logs de Laravel

Todos los errores de SUNAT se registran automáticamente:
# Ver logs en tiempo real
tail -f storage/logs/laravel.log

# Buscar errores específicos
grep "SUNAT" storage/logs/laravel.log

Logs de SunatService

El servicio registra errores con contexto:
Log::error('SUNAT - Comprobante rechazado', [
    'venta' => $venta->serie . '-' . $venta->numero,
    'codigo' => $error->getCode(),
    'mensaje' => $error->getMessage(),
]);

Habilitar Debugging de Greenter

Para ver el XML generado y las respuestas SOAP:
$see = $sunatService->getSee($empresa);

// Obtener último XML generado
$xml = $see->getFactory()->getLastXml();
Log::info('XML Generado:', ['xml' => $xml]);

// Después de enviar, obtener respuesta SOAP
$result = $see->sendXml(...);
if (!$result->isSuccess()) {
    Log::error('Error SUNAT:', [
        'error' => $result->getError(),
        'code' => $result->getError()->getCode(),
    ]);
}

Verificación de Estado de Servicios SUNAT

Si tienes problemas de conexión, verifica el estado de los servicios:
  1. Portal SUNAT: https://www.sunat.gob.pe/
  2. Operaciones en Línea: https://e-menu.sunat.gob.pe/
  3. Estado de Servicios: Buscar anuncios de mantenimiento programado
SUNAT realiza mantenimientos programados, especialmente fines de semana. Planifica tus envíos considerando estos periodos.

Problemas de Rendimiento

Timeout en envío de comprobantes

Solución:
  1. Aumentar timeout de PHP:
    set_time_limit(120); // 2 minutos
    
  2. Para grandes volúmenes, usar colas:
    // En el controlador
    GenerarComprobanteJob::dispatch($venta);
    
    // El job maneja el envío en background
    
  3. Verificar latencia de red a servicios SUNAT

Muchos comprobantes pendientes de envío

Solución:
  1. Implementar cola de procesamiento:
    php artisan queue:listen
    
  2. Procesar en lotes:
    $pendientes = Venta::where('estado_sunat', '0')
        ->limit(50)
        ->get();
    
    foreach ($pendientes as $venta) {
        $sunatService->enviarComprobante($venta);
        sleep(1); // Evitar sobrecarga
    }
    

Preguntas Frecuentes

¿Puedo eliminar un comprobante enviado a SUNAT?

No. Una vez aceptado por SUNAT, el comprobante no se puede eliminar. Debes usar:
  • Nota de crédito: Para anular boletas y facturas
  • Comunicación de baja: Solo para facturas (proceso asíncrono)

¿Cuánto tiempo debo guardar los XML y CDR?

Según la normativa tributaria peruana, mínimo 5 años.

¿Puedo corregir un comprobante después de enviarlo?

No directamente. Debes:
  1. Emitir una nota de crédito para anular el comprobante original
  2. Emitir un nuevo comprobante con los datos correctos

¿Qué hago si SUNAT no responde?

Verifica:
  1. Estado de los servicios de SUNAT
  2. Tu conexión a internet
  3. Logs de error en storage/logs/laravel.log
  4. Intenta más tarde (puede ser mantenimiento)

Recursos Adicionales

Ambiente Beta

Prueba en el ambiente de desarrollo

Producción

Configura el ambiente de producción

Soporte Técnico

Si los problemas persisten:
  1. Documentación oficial de SUNAT:
  2. Greenter (biblioteca usada):
  3. Mesa de ayuda de tu PSE (proveedor del certificado digital)
  4. Logs del sistema:
    storage/logs/laravel.log
    

Build docs developers (and LLMs) love