Skip to main content

Visión General

El sistema se autentica con SUNAT mediante credenciales SOL y certificados digitales PEM para firmar documentos XML.
El sistema soporta modo Beta (ambiente de pruebas) y Producción con cambio automático de credenciales.

Credenciales SOL

Usuario SOL (Clave SOL)

SUNAT asigna a cada empresa:
  • RUC: Número de identificación tributaria (11 dígitos)
  • Usuario SOL: Generalmente el RUC + usuario secundario
  • Clave SOL: Contraseña del sistema

Configuración en el Sistema

config/sunat.php:23
'beta' => [
    'ruc' => '20000000001',
    'usuario_sol' => 'MODDATOS',
    'clave_sol' => 'moddatos',
],
Las credenciales de prueba MODDATOS/moddatos son públicas y funcionan en el ambiente Beta de SUNAT.

Certificado Digital

Ubicación del Certificado

Los certificados PEM se almacenan en:
storage/app/sunat/certificados/
├── {RUC}-cert.pem        # Certificado por empresa
└── cert.pem              # Certificado global de prueba

Carga del Certificado

SunatService.php:67
public function getCertificate(Empresa $empresa): string
{
    $certPath = storage_path("app/sunat/certificados/{$empresa->ruc}-cert.pem");

    if (file_exists($certPath)) {
        return file_get_contents($certPath);
    }

    // Fallback a certificado global de prueba
    $globalCert = config('sunat.certificado_prueba');
    if (file_exists($globalCert)) {
        return file_get_contents($globalCert);
    }

    throw new \RuntimeException('No se encontró certificado PEM para la empresa ' . $empresa->ruc);
}
En producción, cada empresa DEBE tener su propio certificado digital válido emitido por una entidad certificadora autorizada por SUNAT.

Inicialización de Greenter

La clase SunatService configura el cliente Greenter:
SunatService.php:42
public function getSee(Empresa $empresa, string $tipoDoc = 'facturacion'): See
{
    $see = new See();
    
    // Configurar endpoint según tipo de documento y modo
    $endpoint = $this->getEndpoint($empresa, $tipoDoc);
    $see->setService($endpoint);

    // Cargar certificado
    $certificate = $this->getCertificate($empresa);
    $see->setCertificate($certificate);

    // Configurar credenciales según modo
    if ($empresa->modo !== 'production') {
        $beta = config('sunat.beta');
        $see->setClaveSOL($beta['ruc'], $beta['usuario_sol'], $beta['clave_sol']);
    } else {
        $see->setClaveSOL($empresa->ruc, $empresa->user_sol, $empresa->clave_sol);
    }

    return $see;
}

Endpoints SUNAT

Facturación (SOAP)

config/sunat.php:6
'endpoints' => [
    'facturacion' => [
        'beta' => 'https://e-beta.sunat.gob.pe/ol-ti-itcpfegem-beta/billService',
        'production' => 'https://e-factura.sunat.gob.pe/ol-ti-itcpfegem/billService',
    ],
],
Usado para:
  • Facturas (01)
  • Boletas (03)
  • Notas de Crédito (07)
  • Notas de Débito (08)
  • Resumen Diario
  • Comunicación de Baja

Guías de Remisión (SOAP Legacy)

config/sunat.php:11
'guia' => [
    'beta' => 'https://e-beta.sunat.gob.pe/ol-ti-itemision-guia-gem-beta/billService',
    'production' => 'https://e-guiaremision.sunat.gob.pe/ol-ti-itemision-guia-gem/billService',
],
Usado para guías de remisión en formato antiguo (deprecado).

GRE (Guías de Remisión Electrónica) REST API

config/sunat.php:15
'gre' => [
    'auth' => 'https://api-seguridad-test.sunat.gob.pe/v1',
    'cpe' => 'https://api-cpe-test.sunat.gob.pe/v1',
    'client_id' => env('SUNAT_GRE_CLIENT_ID', 'TU_ID_DE_PRUEBA'),
    'client_secret' => env('SUNAT_GRE_CLIENT_SECRET', 'TU_SECRET_DE_PRUEBA'),
],
Usado para guías de remisión en formato nuevo (2022+) mediante API REST.
La GRE requiere credenciales OAuth adicionales (client_id y client_secret) obtenidas del portal SUNAT.

Selección de Endpoint

SunatService.php:61
public function getEndpoint(Empresa $empresa, string $tipoDoc = 'facturacion'): string
{
    $modo = $empresa->modo !== 'production' ? 'beta' : 'production';
    return config("sunat.endpoints.{$tipoDoc}.{$modo}");
}
Tipos de documento soportados:
  • facturacion: Facturas, boletas, notas
  • guia: Guías de remisión SOAP
  • gre: Guías de remisión REST

Autenticación GRE (OAuth 2.0)

Para envío de guías electrónicas modernas:
SunatService.php:781
$authConfig = new Configuration();
$authConfig->setHost(config('sunat.endpoints.gre.auth'));
$authApi = new AuthApi(null, $authConfig);

$username = $empresa->modo !== 'production'
    ? config('sunat.beta.ruc') . config('sunat.beta.usuario_sol')
    : $empresa->ruc . $empresa->user_sol;
    
$password = $empresa->modo !== 'production'
    ? config('sunat.beta.clave_sol')
    : $empresa->clave_sol;

$token = $authApi->getToken(
    'password',
    'https://api-cpe.sunat.gob.pe',
    $empresa->gre_client_id ?: config('sunat.endpoints.gre.client_id'),
    $empresa->gre_client_secret ?: config('sunat.endpoints.gre.client_secret'),
    $username,
    $password
);

$cpeConfig = new Configuration();
$cpeConfig->setHost(config('sunat.endpoints.gre.cpe'));
$cpeConfig->setAccessToken($token->getAccessToken());
El token OAuth tiene una validez corta (generalmente 5-10 minutos). Se debe solicitar un nuevo token para cada envío.

Configuración de Empresa

En la tabla empresas:
CREATE TABLE empresas (
    id_empresa INT PRIMARY KEY,
    ruc VARCHAR(11) NOT NULL,
    razon_social VARCHAR(255),
    user_sol VARCHAR(50),           -- Usuario SOL (sin RUC)
    clave_sol VARCHAR(255),         -- Contraseña encriptada
    gre_client_id VARCHAR(100),     -- OAuth Client ID para GRE
    gre_client_secret VARCHAR(255), -- OAuth Secret para GRE
    modo VARCHAR(20) DEFAULT 'beta' -- 'beta' o 'production'
);
Ejemplo de inserción:
Empresa::create([
    'ruc' => '20612706702',
    'razon_social' => 'SANTO DOMINGO SAC',
    'user_sol' => 'USUARIO01',      // Solo el usuario, sin RUC
    'clave_sol' => bcrypt('mi_clave_segura'),
    'modo' => 'beta',
]);
NUNCA almacene la clave_sol en texto plano. Use bcrypt() o Hash::make() para encriptarla.

Cambio de Modo Beta a Producción

Para activar el modo producción:
$empresa->update(['modo' => 'production']);
El sistema automáticamente:
  1. Cambiará al endpoint de producción
  2. Usará el RUC y credenciales reales de la empresa
  3. Requerirá certificado digital válido en storage/app/sunat/certificados/{RUC}-cert.pem

Obtención de Certificado Digital

Pasos para obtener certificado:

1

Solicitar certificado

Contactar una entidad certificadora autorizada (ej: RENIEC, eCert)
2

Validar identidad

Presentar documentos legales de la empresa
3

Recibir certificado

Generalmente se entrega en formato .PFX o .P12
4

Convertir a PEM

Usar OpenSSL para convertir:
openssl pkcs12 -in certificado.pfx -out cert.pem -nodes
5

Subir al servidor

Colocar en storage/app/sunat/certificados/{RUC}-cert.pem

Validación de Credenciales

Para probar las credenciales antes de envío:
try {
    $see = $sunatService->getSee($empresa);
    // Si no lanza excepción, las credenciales son válidas
    return ['success' => true, 'message' => 'Credenciales válidas'];
} catch (\Exception $e) {
    return ['success' => false, 'message' => $e->getMessage()];
}

Logs de Autenticación

El sistema registra errores de autenticación:
Log::error('SUNAT - Error de autenticación', [
    'empresa' => $empresa->ruc,
    'modo' => $empresa->modo,
    'error' => $exception->getMessage(),
]);
Los logs se almacenan en storage/logs/laravel.log.

Troubleshooting

Causa: Falta el archivo de certificado.Solución:
  • Verificar que existe storage/app/sunat/certificados/{RUC}-cert.pem
  • O configurar certificado global de prueba en config/sunat.php
Causa: Usuario SOL o contraseña incorrectos.Solución:
  • Verificar user_sol en tabla empresas (sin incluir el RUC)
  • Verificar que clave_sol esté correctamente encriptada
  • En beta, usar MODDATOS/moddatos
Causa: Credenciales OAuth incorrectas para GRE.Solución:
  • Obtener client_id y client_secret desde el portal SUNAT
  • Configurar en .env o en tabla empresas
Causa: El certificado digital venció.Solución:
  • Renovar certificado con la entidad certificadora
  • Reemplazar archivo PEM con el nuevo certificado

Variables de Entorno

En .env:
# SUNAT Configuration
SUNAT_IGV=0.18

# GRE OAuth Credentials (prueba)
SUNAT_GRE_CLIENT_ID=test-client-id
SUNAT_GRE_CLIENT_SECRET=test-client-secret

# Paths
SUNAT_CERT_PATH=storage/app/sunat/certificados/cert.pem

Build docs developers (and LLMs) love