aeunius / laravel-peru-rules
Reglas de validación y casts de Laravel para documentos y datos peruanos: RUC, DNI, carné de extranjería, pasaporte, celular, placa vehicular y CCI.
Requires
- php: ^8.2
- illuminate/contracts: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- illuminate/validation: ^12.0|^13.0
- spatie/laravel-package-tools: ^1.16.2
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Reglas de validación de Laravel para documentos y datos peruanos: RUC, DNI, carné de extranjería, pasaporte, celular, placa vehicular y CCI. Validan el formato y los dígitos de control sin conectarse a ningún servicio externo.
Qué valida
| Regla | En texto | Qué comprueba |
|---|---|---|
new Ruc |
ruc |
11 dígitos, prefijo 10, 15, 16, 17 o 20, y dígito verificador |
Ruc::natural() |
ruc:natural |
Lo mismo, pero solo con el prefijo 10 (persona natural) |
Ruc::juridica() |
ruc:juridica |
Lo mismo, pero solo con el prefijo 20 (persona jurídica) |
new Dni |
dni |
8 dígitos |
new CarneExtranjeria |
carne_extranjeria |
Hasta 12 letras o números |
new Pasaporte |
pasaporte |
Hasta 12 letras o números |
DocumentoIdentidad::segun('tipo_doc') |
documento_identidad:tipo_doc |
El número según el tipo de documento de otro campo (catálogo 06 de la SUNAT) |
new Celular |
celular |
9 dígitos que empiezan con 9; acepta +51 y separadores |
new PlacaVehicular |
placa_vehicular |
Autos: ABC-123 o A1B-234, con o sin guion. Especiales con prefijo E: E GA-123, eGA-123. Motos: 2171-AY, 5040-6C, C5-4481 |
new Cci |
cci |
20 dígitos y los dos dígitos de control; acepta espacios y guiones |
Se comprueba que el número sea válido, no que exista: un RUC puede tener un dígito verificador correcto y aun así no estar inscrito o no estar activo en la SUNAT.
Requisitos
- PHP 8.2 o superior
- Laravel 12 o 13
Instalación
composer require aeunius/laravel-peru-rules
El service provider se registra solo.
Uso
use Aeunius\PeruRules\Rules\{Cci, Celular, Dni, PlacaVehicular, Ruc}; $request->validate([ 'ruc' => ['required', new Ruc], 'ruc_empresa' => ['required', Ruc::juridica()], 'dni' => ['required', new Dni], 'celular' => ['required', new Celular], 'placa' => ['nullable', new PlacaVehicular], 'cci' => ['required', new Cci], ]);
También como reglas en texto:
$request->validate([ 'ruc' => 'required|ruc', 'ruc_empresa' => 'required|ruc:juridica', 'dni' => 'nullable|dni', ]);
Los valores se validan tal como llegan: 20-13131295-5 o un RUC con espacios no
pasan. Si tu formulario los admite, límpialos antes de validar. Se aceptan
textos y enteros; un DNI con ceros a la izquierda (00123456) solo llega
completo como texto.
Como cualquier regla de Laravel que no es required, las reglas no se aplican a
un campo vacío.
Documento según su tipo
Cuando el formulario pide el tipo y el número de documento, DocumentoIdentidad
valida el número con la regla que corresponde al tipo. Los tipos son los del
catálogo 06 de la SUNAT, los mismos de la facturación electrónica:
use Aeunius\PeruRules\Enums\TipoDocumento; use Aeunius\PeruRules\Rules\DocumentoIdentidad; use Illuminate\Validation\Rule; $request->validate([ 'tipo_doc' => ['required', Rule::enum(TipoDocumento::class)], 'num_doc' => ['required', DocumentoIdentidad::segun('tipo_doc')], ]);
| Código | Tipo | Se valida con |
|---|---|---|
0 |
Documento tributario de no domiciliado sin RUC | Hasta 15 letras o números |
1 |
DNI | Dni |
4 |
Carné de extranjería | CarneExtranjeria |
6 |
RUC | Ruc |
7 |
Pasaporte | Pasaporte |
A |
Cédula diplomática de identidad | Hasta 15 letras o números |
El mensaje de error es el de la regla de cada tipo. Si el tipo falta o no es
válido, el número tampoco pasa. En arreglos se usan comodines:
DocumentoIdentidad::segun('clientes.*.tipo_doc') valida clientes.2.num_doc
con clientes.2.tipo_doc.
El enum también sirve fuera de la validación:
TipoDocumento::from('6')->descripcion(); // "RUC" TipoDocumento::Pasaporte->longitudMaxima(); // 12 TipoDocumento::Dni->esValido('12345678'); // true
RUC como objeto en tus modelos
ValueObjects\Ruc representa un RUC que ya pasó la validación: no se puede crear
con uno inválido. Úsalo como cast de Eloquent:
use Aeunius\PeruRules\ValueObjects\Ruc; class Cliente extends Model { protected function casts(): array { return [ 'ruc' => Ruc::class, // o RucCast::class ]; } }
$cliente->ruc->valor(); // "20131312955" $cliente->ruc->formateado(); // "20-13131295-5" $cliente->ruc->tipo(); // TipoContribuyente::Juridica $cliente->ruc->esJuridica(); // true $cliente->ruc->dni(); // null; en un RUC 10 da los 8 dígitos del DNI $cliente->ruc = '20100047218'; // o un entero, o un objeto Ruc $cliente->ruc = '20100047219'; // InvalidArgumentException
En la base de datos se guardan los 11 dígitos como texto, y toArray() y
toJson() también devuelven los 11 dígitos. El cast es estricto al leer: si la
tabla tiene un RUC inválido, lanza una excepción en vez de devolver null.
Fuera de Eloquent:
Ruc::from('20131312955'); // Ruc, o InvalidArgumentException Ruc::tryFrom('20131312956'); // null
Si en el mismo archivo usas también la regla Rules\Ruc, importa uno de los dos
con alias: use Aeunius\PeruRules\ValueObjects\Ruc as RucValor;.
Celular, placa y CCI: normalizar antes de guardar
Las reglas Celular, PlacaVehicular y Cci aceptan varias formas de escribir el mismo
dato. Para guardarlo siempre igual, normalízalo, por ejemplo en
prepareForValidation() de un Form Request:
use Aeunius\PeruRules\Support\CciValidator; use Aeunius\PeruRules\Support\CelularValidator; use Aeunius\PeruRules\Support\PlacaVehicularValidator; CelularValidator::normalizar('+51 987 654 321'); // "987654321" PlacaVehicularValidator::normalizar('abc123'); // "ABC-123" PlacaVehicularValidator::normalizar('e GA-123'); // "EGA-123" PlacaVehicularValidator::normalizar('2171ay'); // "2171-AY" CciValidator::normalizar('002-191-000123456789-57'); // "00219100012345678957" CelularValidator::normalizar('014567890'); // null: no es un celular
En las placas de motos del tipo C5-4481 el guion es obligatorio: sin él,
C54481 se lee como la placa de auto C54-481.
Sin Laravel
Los algoritmos no dependen de Laravel y se pueden usar directamente:
use Aeunius\PeruRules\Support\RucValidator; RucValidator::esValido('20131312955'); // true RucValidator::esValido('20131312955', [RucValidator::NATURAL]); // false RucValidator::digitoVerificador('2013131295'); // 5
Mensajes
Los mensajes vienen en español e inglés y siguen el idioma activo de la aplicación. Para cambiarlos, publica las traducciones:
php artisan vendor:publish --tag=peru-rules-translations
y edita lang/vendor/peru-rules/{es,en}/validation.php. En las reglas en texto
también tienen prioridad los mensajes de tu aplicación: validation.ruc en
lang/es/validation.php o el arreglo de mensajes del validador.
Cómo se valida el RUC
El RUC tiene 11 dígitos: un prefijo de 2, 8 dígitos de identificación y un dígito verificador. Para una persona natural, los 8 del medio son su DNI.
- Multiplica los 10 primeros dígitos por los pesos
5 4 3 2 7 6 5 4 3 2. - Suma los productos.
- Calcula
r = 11 − (suma mod 11). Sires 10, el dígito es0; si es 11, es1. rtiene que ser igual al último dígito.
Por ejemplo, para el RUC de la SUNAT, 20131312955:
2×5 + 0×4 + 1×3 + 3×2 + 1×7 + 3×6 + 1×5 + 2×4 + 9×3 + 5×2 = 94
94 mod 11 = 6 → 11 − 6 = 5 → dígito verificador 5 ✓
Cómo se valida el CCI
El CCI tiene 20 dígitos: entidad (3), oficina (3), cuenta (12) y dos dígitos de control. El primero verifica entidad + oficina, y el segundo, la cuenta. Cada uno se calcula así:
- Multiplica los dígitos, de izquierda a derecha, por
1, 2, 1, 2, …. - Si un producto tiene dos cifras, suma sus cifras (
14cuenta como1 + 4). - Suma todo. El dígito de control es lo que falta para la siguiente decena:
(10 − suma mod 10) mod 10.
Para 002-191-000123456789-57:
002191 → 0 + 0 + 2 + 2 + 9 + 2 = 15 → 5
000123456789 → 0+0+0+2+2+6+4+(1+0)+6+(1+4)+8+(1+8) = 43 → 7
No hay una especificación pública del algoritmo. Se comprobó con 11 CCI que empresas publican para recibir pagos, de BCP, BBVA, Interbank, Banco de la Nación, Caja Arequipa y Caja Piura: coinciden los 22 dígitos de control. Solo se valida el formato: el paquete no sabe si la cuenta existe ni si el código de entidad está asignado.
Comparación con otros paquetes
| Paquete | Qué cubre | Diferencia |
|---|---|---|
consulta/laravel |
RUC y DNI | Consulta un servicio externo; este paquete funciona sin conexión |
esolutions/peru |
Dígito verificador del RUC, formato del DNI | Este paquete agrega reglas de Laravel en objeto y en texto, con mensajes traducidos |
En JavaScript
@aeunius/peru-rules da el mismo
resultado en el navegador o en Node, porque se prueba con los casos de este
paquete (tests/fixtures/). Sirve para validar el formulario antes de enviarlo,
e incluye reglas para Vue (Vuetify, Quasar y VeeValidate):
npm install @aeunius/peru-rules
import { ruc } from '@aeunius/peru-rules'; import { usePeruRules } from '@aeunius/peru-rules/vue'; ruc.esValido('20131312955'); // true const { reglas } = usePeruRules(); reglas.ruc('20131312956'); // "El RUC no es válido."
Si cambias una regla, cambia primero los casos en tests/fixtures/ y publica un
tag: el paquete de JavaScript fija sus casos a un tag de este repositorio.
Desarrollo
Todo corre en Docker con la imagen oficial composer:2, así que no hace falta
tener PHP instalado:
make install # dependencias make test # Pest make analyse # PHPStan make lint # Pint, sin cambiar archivos make help # todos los comandos
El CI prueba con Laravel 12 y 13, con PHP 8.2 a 8.5, y también con las versiones mínimas de las dependencias.
Los cambios de cada versión están en el CHANGELOG.
Licencia
MIT. Ver LICENSE.md.