arca-sdk 0.2.8 → 0.3.0

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
@@ -382,8 +382,12 @@ var WsaaService = class {
382
382
  }
383
383
  }
384
384
  /**
385
- * Obtiene un ticket de acceso válido
386
- * Usa cache si el ticket actual es válido
385
+ * Obtiene un ticket de acceso válido.
386
+ *
387
+ * Prioridad de búsqueda:
388
+ * 1. Memoria (TicketManager cache)
389
+ * 2. Persistencia (si config.storage está definido)
390
+ * 3. Nueva solicitud a WSAA
387
391
  *
388
392
  * @returns Ticket de acceso
389
393
  */
@@ -392,8 +396,29 @@ var WsaaService = class {
392
396
  if (cachedTicket) {
393
397
  return cachedTicket;
394
398
  }
399
+ if (this.config.storage) {
400
+ try {
401
+ const storedTicket = await this.config.storage.get(this.config.cuit, this.config.environment);
402
+ if (storedTicket) {
403
+ const now = /* @__PURE__ */ new Date();
404
+ if (new Date(storedTicket.expirationTime) > new Date(now.getTime() + 5 * 6e4)) {
405
+ this.ticketManager.setTicket(storedTicket);
406
+ return storedTicket;
407
+ }
408
+ }
409
+ } catch (error) {
410
+ console.warn("[ARCA-SDK] TokenStorage.get fall\xF3, intentando login directo:", error);
411
+ }
412
+ }
395
413
  const ticket = await this.requestNewTicket();
396
414
  this.ticketManager.setTicket(ticket);
415
+ if (this.config.storage) {
416
+ try {
417
+ await this.config.storage.save(this.config.cuit, this.config.environment, ticket);
418
+ } catch (error) {
419
+ console.warn("[ARCA-SDK] TokenStorage.save fall\xF3:", error);
420
+ }
421
+ }
397
422
  return ticket;
398
423
  }
399
424
  /**
@@ -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 const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n\r\n try {\r\n const envelope = parser.parse(xml);\r\n\r\n // El loginCmsReturn contiene el ticket real como un XML escapado (string)\r\n const loginCmsReturn = envelope?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!loginCmsReturn) {\r\n const fault = envelope?.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, 5000),\r\n receivedStructure: JSON.stringify(envelope).substring(0, 500)\r\n }\r\n );\r\n }\r\n\r\n // Segundo nivel de parseo: El XML interno escapado que viene dentro de loginCmsReturn\r\n const ticketResult = parser.parse(loginCmsReturn);\r\n const ticket = ticketResult?.loginTicketResponse;\r\n\r\n if (!ticket || !ticket.header || !ticket.credentials) {\r\n throw new ArcaAuthError('Ticket WSAA inválido o malformado dentro de loginCmsReturn', {\r\n innerStructure: JSON.stringify(ticketResult).substring(0, 500),\r\n receivedXml: xml.substring(0, 5000)\r\n });\r\n }\r\n\r\n return {\r\n token: ticket.credentials.token,\r\n sign: ticket.credentials.sign,\r\n generationTime: new Date(ticket.header.generationTime),\r\n expirationTime: new Date(ticket.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 (posible XML anidado malformado)',\r\n {\r\n originalError: error instanceof Error ? error.message : String(error),\r\n receivedXml: xml.substring(0, 5000)\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\r\n/**\r\n * Estado de los servidores de ARCA\r\n */\r\nexport interface ServiceStatus {\r\n /** Estado del servidor de aplicaciones */\r\n AppServer: string;\r\n /** Estado del servidor de base de datos */\r\n DbServer: string;\r\n /** Estado del servidor de autenticación */\r\n AuthServer: string;\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 ServiceStatus,\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 * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación ni instancia configurada.\r\n */\r\n static async checkStatus(environment: 'homologacion' | 'produccion' = 'homologacion'): Promise<ServiceStatus> {\r\n const soapRequest = `<?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:FEDummy/>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n\r\n const endpoint = getWsfeEndpoint(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/FEDummy',\r\n },\r\n body: soapRequest,\r\n timeout: 10000,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar estado: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n const data = result?.Envelope?.Body?.FEDummyResponse?.FEDummyResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta FEDummy inválida', 'PARSE_ERROR', { xml: responseXml });\r\n }\r\n\r\n return {\r\n AppServer: data.AppServer,\r\n DbServer: data.DbServer,\r\n AuthServer: data.AuthServer,\r\n };\r\n }\r\n\r\n /**\r\n * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación.\r\n */\r\n async checkStatus(): Promise<ServiceStatus> {\r\n return WsfeService.checkStatus(this.config.environment);\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: Number(det.CbteDesde),\r\n fecha: det.CbteFch,\r\n cae: String(det.CAE),\r\n vencimientoCae: String(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 * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (con o sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional)\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. Limpieza estricta de CUIT y CAE (solo números)\r\n const cleanCuit = cuitEmisor.replace(/\\D/g, '');\r\n const cleanCae = caeResponse.cae.replace(/\\D/g, '');\r\n\r\n // 2. 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 // 3. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\\D/g, '') : '0';\r\n\r\n // 4. Armar objeto JSON con ORDEN ESTRICTO de campos\r\n // El orden de las propiedades en JS se mantiene si se definen así (en la mayoría de los motores modernos)\r\n const qrObj: any = {\r\n ver: 1,\r\n fecha: fechaFormat,\r\n cuit: Number(cleanCuit),\r\n ptoVta: Number(caeResponse.puntoVenta),\r\n tipoCmp: Number(caeResponse.tipoComprobante),\r\n nroCmp: Number(caeResponse.nroComprobante),\r\n importe: Number(parseFloat(total.toFixed(2))),\r\n moneda: 'PES',\r\n ctz: 1\r\n };\r\n\r\n // Omitir datos del receptor si es Consumidor Final (99) y el importe es bajo (evita \"datos incompletos\")\r\n // O simplemente incluirlos si se desea, pero AFIP es más feliz con el objeto limpio si no hay receptor real.\r\n if (docTipo !== TipoDocumento.CONSUMIDOR_FINAL || Number(docNro) > 0) {\r\n qrObj.tipoDocRec = Number(docTipo);\r\n qrObj.nroDocRec = Number(docNro);\r\n }\r\n\r\n qrObj.tipoCodAut = 'E';\r\n qrObj.codAut = Number(cleanCae);\r\n\r\n // 5. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrObj);\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 // 6. Retornar URL lista con URL-Safe Encoding\r\n return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(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\nexport type { TokenStorage } from './auth/storage';\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 const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n\r\n try {\r\n const envelope = parser.parse(xml);\r\n\r\n // El loginCmsReturn contiene el ticket real como un XML escapado (string)\r\n const loginCmsReturn = envelope?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!loginCmsReturn) {\r\n const fault = envelope?.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, 5000),\r\n receivedStructure: JSON.stringify(envelope).substring(0, 500)\r\n }\r\n );\r\n }\r\n\r\n // Segundo nivel de parseo: El XML interno escapado que viene dentro de loginCmsReturn\r\n const ticketResult = parser.parse(loginCmsReturn);\r\n const ticket = ticketResult?.loginTicketResponse;\r\n\r\n if (!ticket || !ticket.header || !ticket.credentials) {\r\n throw new ArcaAuthError('Ticket WSAA inválido o malformado dentro de loginCmsReturn', {\r\n innerStructure: JSON.stringify(ticketResult).substring(0, 500),\r\n receivedXml: xml.substring(0, 5000)\r\n });\r\n }\r\n\r\n return {\r\n token: ticket.credentials.token,\r\n sign: ticket.credentials.sign,\r\n generationTime: new Date(ticket.header.generationTime),\r\n expirationTime: new Date(ticket.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 (posible XML anidado malformado)',\r\n {\r\n originalError: error instanceof Error ? error.message : String(error),\r\n receivedXml: xml.substring(0, 5000)\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 * \r\n * Prioridad de búsqueda:\r\n * 1. Memoria (TicketManager cache)\r\n * 2. Persistencia (si config.storage está definido)\r\n * 3. Nueva solicitud a WSAA\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // 1. Intentar usar ticket en memoria (muy rápido)\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // 2. Intentar usar persistencia externa si está disponible\r\n if (this.config.storage) {\r\n try {\r\n const storedTicket = await this.config.storage.get(this.config.cuit, this.config.environment);\r\n if (storedTicket) {\r\n // Validar si el ticket devuelto por el storage no está expirado\r\n // Agrego un margen de 5 minutos\r\n const now = new Date();\r\n if (new Date(storedTicket.expirationTime) > new Date(now.getTime() + 5 * 60000)) {\r\n this.ticketManager.setTicket(storedTicket);\r\n return storedTicket;\r\n }\r\n }\r\n } catch (error) {\r\n console.warn('[ARCA-SDK] TokenStorage.get falló, intentando login directo:', error);\r\n }\r\n }\r\n\r\n // 3. Generar nuevo ticket (llamada a AFIP)\r\n const ticket = await this.requestNewTicket();\r\n\r\n // Guardar en memoria\r\n this.ticketManager.setTicket(ticket);\r\n\r\n // Guardar en persistencia externa\r\n if (this.config.storage) {\r\n try {\r\n await this.config.storage.save(this.config.cuit, this.config.environment, ticket);\r\n } catch (error) {\r\n console.warn('[ARCA-SDK] TokenStorage.save falló:', error);\r\n }\r\n }\r\n\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\r\n/**\r\n * Estado de los servidores de ARCA\r\n */\r\nexport interface ServiceStatus {\r\n /** Estado del servidor de aplicaciones */\r\n AppServer: string;\r\n /** Estado del servidor de base de datos */\r\n DbServer: string;\r\n /** Estado del servidor de autenticación */\r\n AuthServer: string;\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 ServiceStatus,\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 * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación ni instancia configurada.\r\n */\r\n static async checkStatus(environment: 'homologacion' | 'produccion' = 'homologacion'): Promise<ServiceStatus> {\r\n const soapRequest = `<?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:FEDummy/>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n\r\n const endpoint = getWsfeEndpoint(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/FEDummy',\r\n },\r\n body: soapRequest,\r\n timeout: 10000,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar estado: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n const data = result?.Envelope?.Body?.FEDummyResponse?.FEDummyResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta FEDummy inválida', 'PARSE_ERROR', { xml: responseXml });\r\n }\r\n\r\n return {\r\n AppServer: data.AppServer,\r\n DbServer: data.DbServer,\r\n AuthServer: data.AuthServer,\r\n };\r\n }\r\n\r\n /**\r\n * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación.\r\n */\r\n async checkStatus(): Promise<ServiceStatus> {\r\n return WsfeService.checkStatus(this.config.environment);\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: Number(det.CbteDesde),\r\n fecha: det.CbteFch,\r\n cae: String(det.CAE),\r\n vencimientoCae: String(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 * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (con o sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional)\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. Limpieza estricta de CUIT y CAE (solo números)\r\n const cleanCuit = cuitEmisor.replace(/\\D/g, '');\r\n const cleanCae = caeResponse.cae.replace(/\\D/g, '');\r\n\r\n // 2. 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 // 3. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\\D/g, '') : '0';\r\n\r\n // 4. Armar objeto JSON con ORDEN ESTRICTO de campos\r\n // El orden de las propiedades en JS se mantiene si se definen así (en la mayoría de los motores modernos)\r\n const qrObj: any = {\r\n ver: 1,\r\n fecha: fechaFormat,\r\n cuit: Number(cleanCuit),\r\n ptoVta: Number(caeResponse.puntoVenta),\r\n tipoCmp: Number(caeResponse.tipoComprobante),\r\n nroCmp: Number(caeResponse.nroComprobante),\r\n importe: Number(parseFloat(total.toFixed(2))),\r\n moneda: 'PES',\r\n ctz: 1\r\n };\r\n\r\n // Omitir datos del receptor si es Consumidor Final (99) y el importe es bajo (evita \"datos incompletos\")\r\n // O simplemente incluirlos si se desea, pero AFIP es más feliz con el objeto limpio si no hay receptor real.\r\n if (docTipo !== TipoDocumento.CONSUMIDOR_FINAL || Number(docNro) > 0) {\r\n qrObj.tipoDocRec = Number(docTipo);\r\n qrObj.nroDocRec = Number(docNro);\r\n }\r\n\r\n qrObj.tipoCodAut = 'E';\r\n qrObj.codAut = Number(cleanCae);\r\n\r\n // 5. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrObj);\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 // 6. Retornar URL lista con URL-Safe Encoding\r\n return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;\r\n}\r\n"],"mappings":"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","names":["https","TipoComprobante","Concepto","TipoDocumento"]}
package/dist/index.d.cts CHANGED
@@ -37,6 +37,23 @@ declare class ArcaValidationError extends ArcaError {
37
37
  constructor(message: string, details?: unknown);
38
38
  }
39
39
 
40
+ /**
41
+ * Interface for token persistence.
42
+ * Allows the SDK to automatically handle TA (Ticket de Acceso) lifecycle
43
+ * by delegating storage to an external system (DB, Cache, etc).
44
+ */
45
+ interface TokenStorage {
46
+ /**
47
+ * Retrieves a stored ticket for a specific CUIT and environment.
48
+ * Should return null if not found or expired.
49
+ */
50
+ get(cuit: string, env: string): Promise<LoginTicket | null>;
51
+ /**
52
+ * Persists a new ticket.
53
+ */
54
+ save(cuit: string, env: string, ticket: LoginTicket): Promise<void>;
55
+ }
56
+
40
57
  /**
41
58
  * Configuración para WSAA
42
59
  */
@@ -47,6 +64,8 @@ interface WsaaConfig extends ArcaConfig {
47
64
  key: string;
48
65
  /** Servicio ARCA a autenticar (ej: 'wsfe', 'wsmtxca') */
49
66
  service: string;
67
+ /** Adaptador opcional para persistencia de tokens */
68
+ storage?: TokenStorage;
50
69
  }
51
70
  /**
52
71
  * Ticket de acceso obtenido de WSAA
@@ -88,8 +107,12 @@ declare class WsaaService {
88
107
  */
89
108
  private validateConfig;
90
109
  /**
91
- * Obtiene un ticket de acceso válido
92
- * Usa cache si el ticket actual es válido
110
+ * Obtiene un ticket de acceso válido.
111
+ *
112
+ * Prioridad de búsqueda:
113
+ * 1. Memoria (TicketManager cache)
114
+ * 2. Persistencia (si config.storage está definido)
115
+ * 3. Nueva solicitud a WSAA
93
116
  *
94
117
  * @returns Ticket de acceso
95
118
  */
@@ -362,4 +385,4 @@ declare class WsfeService {
362
385
  */
363
386
  declare function generarUrlQR(caeResponse: CAEResponse, cuitEmisor: string, total: number, comprador?: Comprador): string;
364
387
 
365
- export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, TipoComprobante, TipoDocumento, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
388
+ export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, TipoComprobante, TipoDocumento, type TokenStorage, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,23 @@ declare class ArcaValidationError extends ArcaError {
37
37
  constructor(message: string, details?: unknown);
38
38
  }
39
39
 
40
+ /**
41
+ * Interface for token persistence.
42
+ * Allows the SDK to automatically handle TA (Ticket de Acceso) lifecycle
43
+ * by delegating storage to an external system (DB, Cache, etc).
44
+ */
45
+ interface TokenStorage {
46
+ /**
47
+ * Retrieves a stored ticket for a specific CUIT and environment.
48
+ * Should return null if not found or expired.
49
+ */
50
+ get(cuit: string, env: string): Promise<LoginTicket | null>;
51
+ /**
52
+ * Persists a new ticket.
53
+ */
54
+ save(cuit: string, env: string, ticket: LoginTicket): Promise<void>;
55
+ }
56
+
40
57
  /**
41
58
  * Configuración para WSAA
42
59
  */
@@ -47,6 +64,8 @@ interface WsaaConfig extends ArcaConfig {
47
64
  key: string;
48
65
  /** Servicio ARCA a autenticar (ej: 'wsfe', 'wsmtxca') */
49
66
  service: string;
67
+ /** Adaptador opcional para persistencia de tokens */
68
+ storage?: TokenStorage;
50
69
  }
51
70
  /**
52
71
  * Ticket de acceso obtenido de WSAA
@@ -88,8 +107,12 @@ declare class WsaaService {
88
107
  */
89
108
  private validateConfig;
90
109
  /**
91
- * Obtiene un ticket de acceso válido
92
- * Usa cache si el ticket actual es válido
110
+ * Obtiene un ticket de acceso válido.
111
+ *
112
+ * Prioridad de búsqueda:
113
+ * 1. Memoria (TicketManager cache)
114
+ * 2. Persistencia (si config.storage está definido)
115
+ * 3. Nueva solicitud a WSAA
93
116
  *
94
117
  * @returns Ticket de acceso
95
118
  */
@@ -362,4 +385,4 @@ declare class WsfeService {
362
385
  */
363
386
  declare function generarUrlQR(caeResponse: CAEResponse, cuitEmisor: string, total: number, comprador?: Comprador): string;
364
387
 
365
- export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, TipoComprobante, TipoDocumento, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
388
+ export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, TipoComprobante, TipoDocumento, type TokenStorage, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
package/dist/index.js CHANGED
@@ -338,8 +338,12 @@ var WsaaService = class {
338
338
  }
339
339
  }
340
340
  /**
341
- * Obtiene un ticket de acceso válido
342
- * Usa cache si el ticket actual es válido
341
+ * Obtiene un ticket de acceso válido.
342
+ *
343
+ * Prioridad de búsqueda:
344
+ * 1. Memoria (TicketManager cache)
345
+ * 2. Persistencia (si config.storage está definido)
346
+ * 3. Nueva solicitud a WSAA
343
347
  *
344
348
  * @returns Ticket de acceso
345
349
  */
@@ -348,8 +352,29 @@ var WsaaService = class {
348
352
  if (cachedTicket) {
349
353
  return cachedTicket;
350
354
  }
355
+ if (this.config.storage) {
356
+ try {
357
+ const storedTicket = await this.config.storage.get(this.config.cuit, this.config.environment);
358
+ if (storedTicket) {
359
+ const now = /* @__PURE__ */ new Date();
360
+ if (new Date(storedTicket.expirationTime) > new Date(now.getTime() + 5 * 6e4)) {
361
+ this.ticketManager.setTicket(storedTicket);
362
+ return storedTicket;
363
+ }
364
+ }
365
+ } catch (error) {
366
+ console.warn("[ARCA-SDK] TokenStorage.get fall\xF3, intentando login directo:", error);
367
+ }
368
+ }
351
369
  const ticket = await this.requestNewTicket();
352
370
  this.ticketManager.setTicket(ticket);
371
+ if (this.config.storage) {
372
+ try {
373
+ await this.config.storage.save(this.config.cuit, this.config.environment, ticket);
374
+ } catch (error) {
375
+ console.warn("[ARCA-SDK] TokenStorage.save fall\xF3:", error);
376
+ }
377
+ }
353
378
  return ticket;
354
379
  }
355
380
  /**
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 const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n\r\n try {\r\n const envelope = parser.parse(xml);\r\n\r\n // El loginCmsReturn contiene el ticket real como un XML escapado (string)\r\n const loginCmsReturn = envelope?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!loginCmsReturn) {\r\n const fault = envelope?.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, 5000),\r\n receivedStructure: JSON.stringify(envelope).substring(0, 500)\r\n }\r\n );\r\n }\r\n\r\n // Segundo nivel de parseo: El XML interno escapado que viene dentro de loginCmsReturn\r\n const ticketResult = parser.parse(loginCmsReturn);\r\n const ticket = ticketResult?.loginTicketResponse;\r\n\r\n if (!ticket || !ticket.header || !ticket.credentials) {\r\n throw new ArcaAuthError('Ticket WSAA inválido o malformado dentro de loginCmsReturn', {\r\n innerStructure: JSON.stringify(ticketResult).substring(0, 500),\r\n receivedXml: xml.substring(0, 5000)\r\n });\r\n }\r\n\r\n return {\r\n token: ticket.credentials.token,\r\n sign: ticket.credentials.sign,\r\n generationTime: new Date(ticket.header.generationTime),\r\n expirationTime: new Date(ticket.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 (posible XML anidado malformado)',\r\n {\r\n originalError: error instanceof Error ? error.message : String(error),\r\n receivedXml: xml.substring(0, 5000)\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\r\n/**\r\n * Estado de los servidores de ARCA\r\n */\r\nexport interface ServiceStatus {\r\n /** Estado del servidor de aplicaciones */\r\n AppServer: string;\r\n /** Estado del servidor de base de datos */\r\n DbServer: string;\r\n /** Estado del servidor de autenticación */\r\n AuthServer: string;\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 ServiceStatus,\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 * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación ni instancia configurada.\r\n */\r\n static async checkStatus(environment: 'homologacion' | 'produccion' = 'homologacion'): Promise<ServiceStatus> {\r\n const soapRequest = `<?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:FEDummy/>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n\r\n const endpoint = getWsfeEndpoint(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/FEDummy',\r\n },\r\n body: soapRequest,\r\n timeout: 10000,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar estado: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n const data = result?.Envelope?.Body?.FEDummyResponse?.FEDummyResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta FEDummy inválida', 'PARSE_ERROR', { xml: responseXml });\r\n }\r\n\r\n return {\r\n AppServer: data.AppServer,\r\n DbServer: data.DbServer,\r\n AuthServer: data.AuthServer,\r\n };\r\n }\r\n\r\n /**\r\n * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación.\r\n */\r\n async checkStatus(): Promise<ServiceStatus> {\r\n return WsfeService.checkStatus(this.config.environment);\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: Number(det.CbteDesde),\r\n fecha: det.CbteFch,\r\n cae: String(det.CAE),\r\n vencimientoCae: String(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 * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (con o sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional)\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. Limpieza estricta de CUIT y CAE (solo números)\r\n const cleanCuit = cuitEmisor.replace(/\\D/g, '');\r\n const cleanCae = caeResponse.cae.replace(/\\D/g, '');\r\n\r\n // 2. 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 // 3. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\\D/g, '') : '0';\r\n\r\n // 4. Armar objeto JSON con ORDEN ESTRICTO de campos\r\n // El orden de las propiedades en JS se mantiene si se definen así (en la mayoría de los motores modernos)\r\n const qrObj: any = {\r\n ver: 1,\r\n fecha: fechaFormat,\r\n cuit: Number(cleanCuit),\r\n ptoVta: Number(caeResponse.puntoVenta),\r\n tipoCmp: Number(caeResponse.tipoComprobante),\r\n nroCmp: Number(caeResponse.nroComprobante),\r\n importe: Number(parseFloat(total.toFixed(2))),\r\n moneda: 'PES',\r\n ctz: 1\r\n };\r\n\r\n // Omitir datos del receptor si es Consumidor Final (99) y el importe es bajo (evita \"datos incompletos\")\r\n // O simplemente incluirlos si se desea, pero AFIP es más feliz con el objeto limpio si no hay receptor real.\r\n if (docTipo !== TipoDocumento.CONSUMIDOR_FINAL || Number(docNro) > 0) {\r\n qrObj.tipoDocRec = Number(docTipo);\r\n qrObj.nroDocRec = Number(docNro);\r\n }\r\n\r\n qrObj.tipoCodAut = 'E';\r\n qrObj.codAut = Number(cleanCae);\r\n\r\n // 5. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrObj);\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 // 6. Retornar URL lista con URL-Safe Encoding\r\n return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(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 const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n\r\n try {\r\n const envelope = parser.parse(xml);\r\n\r\n // El loginCmsReturn contiene el ticket real como un XML escapado (string)\r\n const loginCmsReturn = envelope?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!loginCmsReturn) {\r\n const fault = envelope?.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, 5000),\r\n receivedStructure: JSON.stringify(envelope).substring(0, 500)\r\n }\r\n );\r\n }\r\n\r\n // Segundo nivel de parseo: El XML interno escapado que viene dentro de loginCmsReturn\r\n const ticketResult = parser.parse(loginCmsReturn);\r\n const ticket = ticketResult?.loginTicketResponse;\r\n\r\n if (!ticket || !ticket.header || !ticket.credentials) {\r\n throw new ArcaAuthError('Ticket WSAA inválido o malformado dentro de loginCmsReturn', {\r\n innerStructure: JSON.stringify(ticketResult).substring(0, 500),\r\n receivedXml: xml.substring(0, 5000)\r\n });\r\n }\r\n\r\n return {\r\n token: ticket.credentials.token,\r\n sign: ticket.credentials.sign,\r\n generationTime: new Date(ticket.header.generationTime),\r\n expirationTime: new Date(ticket.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 (posible XML anidado malformado)',\r\n {\r\n originalError: error instanceof Error ? error.message : String(error),\r\n receivedXml: xml.substring(0, 5000)\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 * \r\n * Prioridad de búsqueda:\r\n * 1. Memoria (TicketManager cache)\r\n * 2. Persistencia (si config.storage está definido)\r\n * 3. Nueva solicitud a WSAA\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // 1. Intentar usar ticket en memoria (muy rápido)\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // 2. Intentar usar persistencia externa si está disponible\r\n if (this.config.storage) {\r\n try {\r\n const storedTicket = await this.config.storage.get(this.config.cuit, this.config.environment);\r\n if (storedTicket) {\r\n // Validar si el ticket devuelto por el storage no está expirado\r\n // Agrego un margen de 5 minutos\r\n const now = new Date();\r\n if (new Date(storedTicket.expirationTime) > new Date(now.getTime() + 5 * 60000)) {\r\n this.ticketManager.setTicket(storedTicket);\r\n return storedTicket;\r\n }\r\n }\r\n } catch (error) {\r\n console.warn('[ARCA-SDK] TokenStorage.get falló, intentando login directo:', error);\r\n }\r\n }\r\n\r\n // 3. Generar nuevo ticket (llamada a AFIP)\r\n const ticket = await this.requestNewTicket();\r\n\r\n // Guardar en memoria\r\n this.ticketManager.setTicket(ticket);\r\n\r\n // Guardar en persistencia externa\r\n if (this.config.storage) {\r\n try {\r\n await this.config.storage.save(this.config.cuit, this.config.environment, ticket);\r\n } catch (error) {\r\n console.warn('[ARCA-SDK] TokenStorage.save falló:', error);\r\n }\r\n }\r\n\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\r\n/**\r\n * Estado de los servidores de ARCA\r\n */\r\nexport interface ServiceStatus {\r\n /** Estado del servidor de aplicaciones */\r\n AppServer: string;\r\n /** Estado del servidor de base de datos */\r\n DbServer: string;\r\n /** Estado del servidor de autenticación */\r\n AuthServer: string;\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 ServiceStatus,\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 * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación ni instancia configurada.\r\n */\r\n static async checkStatus(environment: 'homologacion' | 'produccion' = 'homologacion'): Promise<ServiceStatus> {\r\n const soapRequest = `<?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:FEDummy/>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n\r\n const endpoint = getWsfeEndpoint(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/FEDummy',\r\n },\r\n body: soapRequest,\r\n timeout: 10000,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar estado: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n const data = result?.Envelope?.Body?.FEDummyResponse?.FEDummyResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta FEDummy inválida', 'PARSE_ERROR', { xml: responseXml });\r\n }\r\n\r\n return {\r\n AppServer: data.AppServer,\r\n DbServer: data.DbServer,\r\n AuthServer: data.AuthServer,\r\n };\r\n }\r\n\r\n /**\r\n * Verifica el estado de los servidores de ARCA (FEDummy)\r\n * No requiere autenticación.\r\n */\r\n async checkStatus(): Promise<ServiceStatus> {\r\n return WsfeService.checkStatus(this.config.environment);\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: Number(det.CbteDesde),\r\n fecha: det.CbteFch,\r\n cae: String(det.CAE),\r\n vencimientoCae: String(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 * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (con o sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional)\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. Limpieza estricta de CUIT y CAE (solo números)\r\n const cleanCuit = cuitEmisor.replace(/\\D/g, '');\r\n const cleanCae = caeResponse.cae.replace(/\\D/g, '');\r\n\r\n // 2. 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 // 3. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\\D/g, '') : '0';\r\n\r\n // 4. Armar objeto JSON con ORDEN ESTRICTO de campos\r\n // El orden de las propiedades en JS se mantiene si se definen así (en la mayoría de los motores modernos)\r\n const qrObj: any = {\r\n ver: 1,\r\n fecha: fechaFormat,\r\n cuit: Number(cleanCuit),\r\n ptoVta: Number(caeResponse.puntoVenta),\r\n tipoCmp: Number(caeResponse.tipoComprobante),\r\n nroCmp: Number(caeResponse.nroComprobante),\r\n importe: Number(parseFloat(total.toFixed(2))),\r\n moneda: 'PES',\r\n ctz: 1\r\n };\r\n\r\n // Omitir datos del receptor si es Consumidor Final (99) y el importe es bajo (evita \"datos incompletos\")\r\n // O simplemente incluirlos si se desea, pero AFIP es más feliz con el objeto limpio si no hay receptor real.\r\n if (docTipo !== TipoDocumento.CONSUMIDOR_FINAL || Number(docNro) > 0) {\r\n qrObj.tipoDocRec = Number(docTipo);\r\n qrObj.nroDocRec = Number(docNro);\r\n }\r\n\r\n qrObj.tipoCodAut = 'E';\r\n qrObj.codAut = Number(cleanCae);\r\n\r\n // 5. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrObj);\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 // 6. Retornar URL lista con URL-Safe Encoding\r\n return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(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.8",
3
+ "version": "0.3.0",
4
4
  "description": "SDK moderna en TypeScript para ARCA (ex-AFIP)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",