arca-sdk 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -130,7 +130,10 @@ function parseWsaaResponse(xml) {
130
130
  }
131
131
  throw new ArcaAuthError(
132
132
  "Respuesta WSAA inv\xE1lida: estructura no reconocida",
133
- { receivedStructure: result }
133
+ {
134
+ receivedXml: xml.substring(0, 1e3),
135
+ receivedStructure: JSON.stringify(result).substring(0, 500)
136
+ }
134
137
  );
135
138
  }
136
139
  return {
@@ -143,7 +146,10 @@ function parseWsaaResponse(xml) {
143
146
  if (error instanceof ArcaAuthError) throw error;
144
147
  throw new ArcaAuthError(
145
148
  "Error al parsear respuesta WSAA",
146
- { originalError: error }
149
+ {
150
+ originalError: error instanceof Error ? error.message : String(error),
151
+ receivedXml: xml.substring(0, 2e3)
152
+ }
147
153
  );
148
154
  }
149
155
  }
@@ -276,6 +282,7 @@ async function callArcaApi(url, options) {
276
282
  timeout
277
283
  };
278
284
  const req = import_https.default.request(reqOptions, (res) => {
285
+ res.setEncoding("utf8");
279
286
  let data = "";
280
287
  res.on("data", (chunk) => {
281
288
  data += chunk;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["/**\r\n * arca-sdk - SDK moderna para ARCA (ex-AFIP)\r\n */\r\n\r\n// Servicios principales\r\nexport { WsaaService } from './auth/wsaa';\r\nexport { WsfeService } from './services/wsfe';\r\n\r\n// Tipos comunes\r\nexport type {\r\n Environment,\r\n ArcaConfig,\r\n} from './types/common';\r\n\r\n// Tipos WSAA\r\nexport type {\r\n WsaaConfig,\r\n LoginTicket,\r\n} from './types/wsaa';\r\n\r\n// Tipos WSFE\r\nexport type {\r\n WsfeConfig,\r\n FacturaItem,\r\n Comprador,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n} from './types/wsfe';\r\n\r\n// Enums WSFE\r\nexport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento,\r\n} from './types/wsfe';\r\n\r\n// Errores\r\nexport {\r\n ArcaError,\r\n ArcaAuthError,\r\n ArcaValidationError,\r\n} from './types/common';\r\n\r\n// Utilidades útiles para Frontend/Impresión\r\nexport { generarUrlQR } from './utils/qr';\r\n","import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n { receivedStructure: result }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n { originalError: error }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000;\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n if (isNode) {\r\n return new Promise((resolve, reject) => {\r\n const parsedUrl = new URL(url);\r\n\r\n const agent = new https.Agent({\r\n // SECLEVEL=0 es el nivel más permisivo de OpenSSL.\r\n // !DH desactiva Diffie-Hellman para forzar RSA o ECDHE si están disponibles,\r\n // evitando el problema de \"dh key too small\" de raíz.\r\n ciphers: 'DEFAULT:!DH@SECLEVEL=0',\r\n // AFIP todavía tiene endpoints que podrían requerir TLS 1.0/1.1\r\n minVersion: 'TLSv1',\r\n // @ts-ignore - Propiedad específica para mitigar \"dh key too small\" en Node 18+\r\n minDHSize: 1024,\r\n rejectUnauthorized: true,\r\n });\r\n\r\n const reqOptions: https.RequestOptions = {\r\n method: options.method,\r\n hostname: parsedUrl.hostname,\r\n path: parsedUrl.pathname + parsedUrl.search,\r\n headers: options.headers,\r\n agent,\r\n timeout,\r\n };\r\n\r\n const req = https.request(reqOptions, (res) => {\r\n let data = '';\r\n res.on('data', (chunk) => { data += chunk; });\r\n res.on('end', () => {\r\n // Simular objeto Response de fetch\r\n const response = {\r\n ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,\r\n status: res.statusCode || 0,\r\n statusText: res.statusMessage || '',\r\n text: async () => data,\r\n json: async () => JSON.parse(data),\r\n };\r\n resolve(response as Response);\r\n });\r\n });\r\n\r\n req.on('error', (error: any) => {\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). Verifique su versión de OpenSSL/Node.';\r\n }\r\n reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {\r\n url,\r\n originalError: error\r\n }));\r\n });\r\n\r\n req.on('timeout', () => {\r\n req.destroy();\r\n reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));\r\n });\r\n\r\n req.write(options.body);\r\n req.end();\r\n });\r\n }\r\n\r\n // Navegador o entorno no-Node (utiliza fetch nativo)\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n return response;\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["https","TipoComprobante","Concepto","TipoDocumento"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["/**\r\n * arca-sdk - SDK moderna para ARCA (ex-AFIP)\r\n */\r\n\r\n// Servicios principales\r\nexport { WsaaService } from './auth/wsaa';\r\nexport { WsfeService } from './services/wsfe';\r\n\r\n// Tipos comunes\r\nexport type {\r\n Environment,\r\n ArcaConfig,\r\n} from './types/common';\r\n\r\n// Tipos WSAA\r\nexport type {\r\n WsaaConfig,\r\n LoginTicket,\r\n} from './types/wsaa';\r\n\r\n// Tipos WSFE\r\nexport type {\r\n WsfeConfig,\r\n FacturaItem,\r\n Comprador,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n} from './types/wsfe';\r\n\r\n// Enums WSFE\r\nexport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento,\r\n} from './types/wsfe';\r\n\r\n// Errores\r\nexport {\r\n ArcaError,\r\n ArcaAuthError,\r\n ArcaValidationError,\r\n} from './types/common';\r\n\r\n// Utilidades útiles para Frontend/Impresión\r\nexport { generarUrlQR } from './utils/qr';\r\n","import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n {\r\n receivedXml: xml.substring(0, 1000),\r\n receivedStructure: JSON.stringify(result).substring(0, 500)\r\n }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n {\r\n originalError: error instanceof Error ? error.message : String(error),\r\n receivedXml: xml.substring(0, 2000)\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000;\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n if (isNode) {\r\n return new Promise((resolve, reject) => {\r\n const parsedUrl = new URL(url);\r\n\r\n const agent = new https.Agent({\r\n // SECLEVEL=0 es el nivel más permisivo de OpenSSL.\r\n // !DH desactiva Diffie-Hellman para forzar RSA o ECDHE si están disponibles,\r\n // evitando el problema de \"dh key too small\" de raíz.\r\n ciphers: 'DEFAULT:!DH@SECLEVEL=0',\r\n // AFIP todavía tiene endpoints que podrían requerir TLS 1.0/1.1\r\n minVersion: 'TLSv1',\r\n // @ts-ignore - Propiedad específica para mitigar \"dh key too small\" en Node 18+\r\n minDHSize: 1024,\r\n rejectUnauthorized: true,\r\n });\r\n\r\n const reqOptions: https.RequestOptions = {\r\n method: options.method,\r\n hostname: parsedUrl.hostname,\r\n path: parsedUrl.pathname + parsedUrl.search,\r\n headers: options.headers,\r\n agent,\r\n timeout,\r\n };\r\n\r\n const req = https.request(reqOptions, (res) => {\r\n res.setEncoding('utf8');\r\n let data = '';\r\n res.on('data', (chunk) => { data += chunk; });\r\n res.on('end', () => {\r\n // Simular objeto Response de fetch\r\n const response = {\r\n ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,\r\n status: res.statusCode || 0,\r\n statusText: res.statusMessage || '',\r\n text: async () => data,\r\n json: async () => JSON.parse(data),\r\n };\r\n resolve(response as Response);\r\n });\r\n });\r\n\r\n req.on('error', (error: any) => {\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). Verifique su versión de OpenSSL/Node.';\r\n }\r\n reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {\r\n url,\r\n originalError: error\r\n }));\r\n });\r\n\r\n req.on('timeout', () => {\r\n req.destroy();\r\n reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));\r\n });\r\n\r\n req.write(options.body);\r\n req.end();\r\n });\r\n }\r\n\r\n // Navegador o entorno no-Node (utiliza fetch nativo)\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n return response;\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["https","TipoComprobante","Concepto","TipoDocumento"]}
package/dist/index.js CHANGED
@@ -86,7 +86,10 @@ function parseWsaaResponse(xml) {
86
86
  }
87
87
  throw new ArcaAuthError(
88
88
  "Respuesta WSAA inv\xE1lida: estructura no reconocida",
89
- { receivedStructure: result }
89
+ {
90
+ receivedXml: xml.substring(0, 1e3),
91
+ receivedStructure: JSON.stringify(result).substring(0, 500)
92
+ }
90
93
  );
91
94
  }
92
95
  return {
@@ -99,7 +102,10 @@ function parseWsaaResponse(xml) {
99
102
  if (error instanceof ArcaAuthError) throw error;
100
103
  throw new ArcaAuthError(
101
104
  "Error al parsear respuesta WSAA",
102
- { originalError: error }
105
+ {
106
+ originalError: error instanceof Error ? error.message : String(error),
107
+ receivedXml: xml.substring(0, 2e3)
108
+ }
103
109
  );
104
110
  }
105
111
  }
@@ -232,6 +238,7 @@ async function callArcaApi(url, options) {
232
238
  timeout
233
239
  };
234
240
  const req = https.request(reqOptions, (res) => {
241
+ res.setEncoding("utf8");
235
242
  let data = "";
236
243
  res.on("data", (chunk) => {
237
244
  data += chunk;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n { receivedStructure: result }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n { originalError: error }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000;\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n if (isNode) {\r\n return new Promise((resolve, reject) => {\r\n const parsedUrl = new URL(url);\r\n\r\n const agent = new https.Agent({\r\n // SECLEVEL=0 es el nivel más permisivo de OpenSSL.\r\n // !DH desactiva Diffie-Hellman para forzar RSA o ECDHE si están disponibles,\r\n // evitando el problema de \"dh key too small\" de raíz.\r\n ciphers: 'DEFAULT:!DH@SECLEVEL=0',\r\n // AFIP todavía tiene endpoints que podrían requerir TLS 1.0/1.1\r\n minVersion: 'TLSv1',\r\n // @ts-ignore - Propiedad específica para mitigar \"dh key too small\" en Node 18+\r\n minDHSize: 1024,\r\n rejectUnauthorized: true,\r\n });\r\n\r\n const reqOptions: https.RequestOptions = {\r\n method: options.method,\r\n hostname: parsedUrl.hostname,\r\n path: parsedUrl.pathname + parsedUrl.search,\r\n headers: options.headers,\r\n agent,\r\n timeout,\r\n };\r\n\r\n const req = https.request(reqOptions, (res) => {\r\n let data = '';\r\n res.on('data', (chunk) => { data += chunk; });\r\n res.on('end', () => {\r\n // Simular objeto Response de fetch\r\n const response = {\r\n ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,\r\n status: res.statusCode || 0,\r\n statusText: res.statusMessage || '',\r\n text: async () => data,\r\n json: async () => JSON.parse(data),\r\n };\r\n resolve(response as Response);\r\n });\r\n });\r\n\r\n req.on('error', (error: any) => {\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). Verifique su versión de OpenSSL/Node.';\r\n }\r\n reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {\r\n url,\r\n originalError: error\r\n }));\r\n });\r\n\r\n req.on('timeout', () => {\r\n req.destroy();\r\n reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));\r\n });\r\n\r\n req.write(options.body);\r\n req.end();\r\n });\r\n }\r\n\r\n // Navegador o entorno no-Node (utiliza fetch nativo)\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n return response;\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["TipoComprobante","Concepto","TipoDocumento"]}
1
+ {"version":3,"sources":["../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n {\r\n receivedXml: xml.substring(0, 1000),\r\n receivedStructure: JSON.stringify(result).substring(0, 500)\r\n }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n {\r\n originalError: error instanceof Error ? error.message : String(error),\r\n receivedXml: xml.substring(0, 2000)\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000;\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n if (isNode) {\r\n return new Promise((resolve, reject) => {\r\n const parsedUrl = new URL(url);\r\n\r\n const agent = new https.Agent({\r\n // SECLEVEL=0 es el nivel más permisivo de OpenSSL.\r\n // !DH desactiva Diffie-Hellman para forzar RSA o ECDHE si están disponibles,\r\n // evitando el problema de \"dh key too small\" de raíz.\r\n ciphers: 'DEFAULT:!DH@SECLEVEL=0',\r\n // AFIP todavía tiene endpoints que podrían requerir TLS 1.0/1.1\r\n minVersion: 'TLSv1',\r\n // @ts-ignore - Propiedad específica para mitigar \"dh key too small\" en Node 18+\r\n minDHSize: 1024,\r\n rejectUnauthorized: true,\r\n });\r\n\r\n const reqOptions: https.RequestOptions = {\r\n method: options.method,\r\n hostname: parsedUrl.hostname,\r\n path: parsedUrl.pathname + parsedUrl.search,\r\n headers: options.headers,\r\n agent,\r\n timeout,\r\n };\r\n\r\n const req = https.request(reqOptions, (res) => {\r\n res.setEncoding('utf8');\r\n let data = '';\r\n res.on('data', (chunk) => { data += chunk; });\r\n res.on('end', () => {\r\n // Simular objeto Response de fetch\r\n const response = {\r\n ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,\r\n status: res.statusCode || 0,\r\n statusText: res.statusMessage || '',\r\n text: async () => data,\r\n json: async () => JSON.parse(data),\r\n };\r\n resolve(response as Response);\r\n });\r\n });\r\n\r\n req.on('error', (error: any) => {\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). Verifique su versión de OpenSSL/Node.';\r\n }\r\n reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {\r\n url,\r\n originalError: error\r\n }));\r\n });\r\n\r\n req.on('timeout', () => {\r\n req.destroy();\r\n reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));\r\n });\r\n\r\n req.write(options.body);\r\n req.end();\r\n });\r\n }\r\n\r\n // Navegador o entorno no-Node (utiliza fetch nativo)\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n return response;\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["TipoComprobante","Concepto","TipoDocumento"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arca-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "SDK moderna en TypeScript para ARCA (ex-AFIP)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",