arca-cert
v0.1.0
Published
Generá certificados para los web services de ARCA (ex AFIP) con solo tu CUIT y clave fiscal
Downloads
127
Maintainers
Readme
arca-cert
Generá certificados para los web services de ARCA (ex AFIP) con solo tu CUIT y clave fiscal.
Sin proxies, sin servicios de terceros, sin Chrome. Todo se ejecuta localmente.
Instalación
npm install arca-certUso rápido
import { createCertificate, authenticate } from 'arca-cert';
// 1. Crear certificado
const { cert, key } = await createCertificate({
cuit: '20123456789',
password: process.env.CLAVE_FISCAL!,
alias: 'mi-sistema',
environment: 'production',
});
// 2. Autenticarse con un web service
const credentials = await authenticate({
cert,
key,
service: 'wsfe',
environment: 'production',
});
// credentials.token y credentials.sign están listos para usarAPI
createCertificate(options)
Crea un certificado nuevo. Genera la clave RSA y el CSR localmente, se logea al portal de ARCA, y obtiene el certificado firmado.
const result = await createCertificate({
cuit: '20123456789', // 11 dígitos (acepta guiones)
password: 'mi-clave-fiscal', // Clave fiscal nivel 2+
alias: 'mi-sistema', // Alfanumérico, guiones y guiones bajos
environment: 'production', // 'testing' o 'production' (default: 'testing')
organization: 'Mi Empresa', // Opcional (default: "CUIT {cuit}")
debug: true, // Opcional, muestra logs de cada paso
});
result.cert // string — Certificado X.509 PEM
result.key // string — Clave privada RSA 2048 PEM
result.alias // string
result.cuit // string
result.environment // 'testing' | 'production'renewCertificate(options)
Crea un certificado nuevo con un alias con sufijo timestamp. ARCA no permite revocar por API, así que la renovación crea uno nuevo y el viejo expira solo.
const renewed = await renewCertificate({
cuit: '20123456789',
password: process.env.CLAVE_FISCAL!,
alias: 'mi-sistema',
environment: 'production',
});
// renewed.alias → 'mi-sistema-r1743220800'authenticate(options)
Autentica con WSAA usando un certificado existente. Devuelve token + sign válidos por ~12 horas.
const credentials = await authenticate({
cert: certPem,
key: keyPem,
service: 'wsfe',
environment: 'production',
});
credentials.token // string — Token para Auth header
credentials.sign // string — Firma para Auth header
credentials.expirationTime // Date — Válido ~12 horasgetCertExpiry(certPem)
Parsea un certificado X.509 y devuelve sus fechas de validez.
import { getCertExpiry } from 'arca-cert';
const { notBefore, notAfter } = getCertExpiry(certPem);
const daysLeft = Math.floor((notAfter.getTime() - Date.now()) / 86_400_000);
if (daysLeft < 30) {
await renewCertificate({ ... });
}parseSoapFault(body)
Parsea respuestas SOAP Fault de ARCA en un error estructurado. Útil si hacés llamadas SOAP propias (wsfe, padrón, etc.) fuera del SDK.
import { parseSoapFault } from 'arca-cert';
const error = parseSoapFault(soapResponseXml);
if (error) {
console.log(error.message); // "WSAA: Certificado no emitido..."
console.log(error.code); // "WSAA_ERROR"
console.log(error.soapFault?.faultcode); // "cms.cert.untrusted"
console.log(error.soapFault?.faultstring); // "Certificado no emitido por AC de confianza"
}validateCuit(cuit)
Valida formato y dígito verificador de un CUIT argentino.
import { validateCuit } from 'arca-cert';
validateCuit('20278650988'); // '20278650988'
validateCuit('20-27865098-8'); // '20278650988' (acepta guiones)
validateCuit('12345678901'); // throws ArcaCertError (dígito verificador inválido)Uso avanzado
import { generateCSR, signTRA, loginWsaa } from 'arca-cert';
// Generar CSR sin crear certificado (para subir manualmente al portal)
const { csr, keyPair } = generateCSR('20123456789', 'mi-alias', 'Mi Empresa SA');
// Firmar TRA para WSAA manualmente
const signedCms = signTRA(traXml, certPem, privateKeyPem);
// Login WSAA directo
const credentials = await loginWsaa(certPem, keyPem, 'wsfe', 'production');Servicios WSAA
| Servicio | Descripción |
|---|---|
| wsfe | Factura electrónica (WSFEv1) |
| wsfex | Factura electrónica de exportación |
| ws_sr_padron_a4 | Padrón alcance 4 |
| ws_sr_padron_a5 | Padrón alcance 5 |
| ws_sr_padron_a10 | Padrón alcance 10 |
| ws_sr_padron_a13 | Padrón alcance 13 |
| ws_sr_constancia_inscripcion | Constancia de inscripción |
También acepta cualquier string para servicios no listados.
Ambientes
| Ambiente | Login | Certificados | WSAA |
| --- | --- | --- | --- |
| testing | authhomo-ext.afip.gob.ar | WSASS (wsass-homo.afip.gob.ar) | wsaahomo.afip.gov.ar |
| production | auth.afip.gob.ar | arfe_certificado (serviciosweb.afip.gob.ar) | wsaa.afip.gov.ar |
Los certificados de un ambiente no funcionan en el otro.
Manejo de errores
Todos los errores son ArcaCertError con code tipado y, para errores SOAP, un soapFault con el detalle original de ARCA:
import { ArcaCertError } from 'arca-cert';
try {
await authenticate({ ... });
} catch (err) {
if (err instanceof ArcaCertError) {
console.log(err.code); // Código tipado
console.log(err.message); // Mensaje legible con hint
if (err.soapFault) {
// Detalle original de ARCA
console.log(err.soapFault.faultcode); // "coe.notAuthorized"
console.log(err.soapFault.faultstring); // "Computador no autorizado..."
}
}
}Códigos de error
| Código | Descripción |
|---|---|
| INVALID_CUIT | CUIT con formato o dígito verificador inválido |
| INVALID_ALIAS | Alias con caracteres no permitidos o muy largo |
| INVALID_SERVICE | Nombre de servicio WSAA inválido |
| LOGIN_FAILED | Credenciales incorrectas o cuenta bloqueada |
| PORTAL_ERROR | Error en el portal de ARCA |
| WSAA_ERROR | Error SOAP de WSAA (ver soapFault para detalle) |
| WSASS_ERROR | Error en la creación del certificado |
| CERTIFICATE_ERROR | CSR rechazado, alias duplicado, etc. |
| CRYPTO_ERROR | Error criptográfico (claves, firma, parseo) |
| NETWORK_ERROR | Timeout o fallo de red |
| SESSION_EXPIRED | Sesión del portal expirada |
| UNSAFE_URL | URL no HTTPS o fuera de dominio ARCA |
SOAP faults conocidos
| Faultcode | Significado | Hint |
| --- | --- | --- |
| cms.cert.untrusted | Cert no emitido por ARCA | Verificar ambiente (testing vs production) |
| coe.notAuthorized | Cert no autorizado para el servicio | Asociar en "Administrador de Relaciones" |
| cms.sign.invalid | Firma CMS inválida | Verificar par cert/key |
| cms.cert.expired | Cert expirado | Crear uno nuevo |
| cms.cert.revoked | Cert revocado | Crear uno nuevo |
| coe.alreadyAuthenticated | Ya hay ticket activo | Esperar expiración |
| coe.ticketExpired | Ticket expirado | Re-autenticar |
Seguridad
- HTTPS obligatorio — todas las conexiones validan protocolo y dominio (solo
*.afip.gob.ar/*.afip.gov.ar) - Credenciales seguras — nunca se loguean, almacenan en disco, ni se incluyen en errores
- Clave privada local — se genera en tu máquina y nunca sale de ella
- Validación de entrada — CUIT (con dígito verificador), alias (alfanumérico), servicio (previene XML injection)
- Timeouts — 120 segundos por defecto en todas las requests HTTP
- Retry — 3 intentos con backoff en el login del portal (solo errores de red, no credenciales)
- Sin dependencias de red — no usa proxies ni servicios de terceros
Ver SECURITY.md para más detalles.
Cómo funciona
Producción
- Genera clave RSA 2048 bits + CSR (PKCS#10) localmente con
node-forge - Login al portal ARCA (JSF forms con ViewState + captcha + JWT)
- Obtiene token para
arfe_certificadovia API del portal - Navega a "Administración de Certificados Digitales"
- Sube alias + CSR como archivo (multipart/form-data)
- Clickea "Ver" en el certificado creado (ASP.NET
__doPostBack) - Descarga el certificado PEM
- Devuelve cert + key
Testing
Igual pero usa WSASS (wsass-homo.afip.gob.ar) con un formulario de un solo paso.
Requisitos
- Node.js >= 18
- Clave fiscal nivel 2 o superior
- Tener el servicio de certificados habilitado en el portal de ARCA
Licencia
MIT
