arca-sdk 0.2.6 → 0.2.8

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
@@ -517,7 +517,7 @@ function redondear(valor) {
517
517
  }
518
518
 
519
519
  // src/services/wsfe.ts
520
- var WsfeService = class {
520
+ var WsfeService = class _WsfeService {
521
521
  config;
522
522
  constructor(config) {
523
523
  this.validateConfig(config);
@@ -555,9 +555,9 @@ var WsfeService = class {
555
555
  }
556
556
  /**
557
557
  * Verifica el estado de los servidores de ARCA (FEDummy)
558
- * No requiere autenticación.
558
+ * No requiere autenticación ni instancia configurada.
559
559
  */
560
- async checkStatus() {
560
+ static async checkStatus(environment = "homologacion") {
561
561
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
562
562
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
563
563
  xmlns:ar="http://ar.gov.afip.dif.FEV1/">
@@ -566,7 +566,7 @@ var WsfeService = class {
566
566
  <ar:FEDummy/>
567
567
  </soapenv:Body>
568
568
  </soapenv:Envelope>`;
569
- const endpoint = getWsfeEndpoint(this.config.environment);
569
+ const endpoint = getWsfeEndpoint(environment);
570
570
  const response = await callArcaApi(endpoint, {
571
571
  method: "POST",
572
572
  headers: {
@@ -574,7 +574,7 @@ var WsfeService = class {
574
574
  "SOAPAction": "http://ar.gov.afip.dif.FEV1/FEDummy"
575
575
  },
576
576
  body: soapRequest,
577
- timeout: this.config.timeout || 1e4
577
+ timeout: 1e4
578
578
  });
579
579
  if (!response.ok) {
580
580
  throw new ArcaError(`Error HTTP al consultar estado: ${response.status}`, "HTTP_ERROR");
@@ -591,6 +591,13 @@ var WsfeService = class {
591
591
  AuthServer: data.AuthServer
592
592
  };
593
593
  }
594
+ /**
595
+ * Verifica el estado de los servidores de ARCA (FEDummy)
596
+ * No requiere autenticación.
597
+ */
598
+ async checkStatus() {
599
+ return _WsfeService.checkStatus(this.config.environment);
600
+ }
594
601
  /**
595
602
  * Emite un Ticket C completo (con detalle de items)
596
603
  * Los items no se envían a ARCA, pero se retornan en la respuesta.
@@ -922,30 +929,30 @@ var WsfeService = class {
922
929
 
923
930
  // src/utils/qr.ts
924
931
  function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
932
+ const cleanCuit = cuitEmisor.replace(/\D/g, "");
933
+ const cleanCae = caeResponse.cae.replace(/\D/g, "");
925
934
  const fDate = caeResponse.fecha;
926
935
  const fechaFormat = fDate.length === 8 ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}` : fDate;
927
936
  const docTipo = comprador?.tipoDocumento || 99 /* CONSUMIDOR_FINAL */;
928
- const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;
929
- const qrData = {
937
+ const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\D/g, "") : "0";
938
+ const qrObj = {
930
939
  ver: 1,
931
- // Versión estándar exigida por AFIP
932
940
  fecha: fechaFormat,
933
- cuit: parseInt(cuitEmisor, 10),
934
- ptoVta: caeResponse.puntoVenta,
935
- tipoCmp: caeResponse.tipoComprobante,
936
- nroCmp: caeResponse.nroComprobante,
937
- importe: parseFloat(total.toFixed(2)),
941
+ cuit: Number(cleanCuit),
942
+ ptoVta: Number(caeResponse.puntoVenta),
943
+ tipoCmp: Number(caeResponse.tipoComprobante),
944
+ nroCmp: Number(caeResponse.nroComprobante),
945
+ importe: Number(parseFloat(total.toFixed(2))),
938
946
  moneda: "PES",
939
- // Por defecto PES (Pesos Argentinos)
940
- ctz: 1,
941
- // Cotización (siempre 1 para PES)
942
- tipoDocRec: docTipo,
943
- nroDocRec: docNro,
944
- tipoCodAut: "E",
945
- // 'E' para comprobantes electrónicos
946
- codAut: parseInt(caeResponse.cae, 10)
947
+ ctz: 1
947
948
  };
948
- const jsonString = JSON.stringify(qrData);
949
+ if (docTipo !== 99 /* CONSUMIDOR_FINAL */ || Number(docNro) > 0) {
950
+ qrObj.tipoDocRec = Number(docTipo);
951
+ qrObj.nroDocRec = Number(docNro);
952
+ }
953
+ qrObj.tipoCodAut = "E";
954
+ qrObj.codAut = Number(cleanCae);
955
+ const jsonString = JSON.stringify(qrObj);
949
956
  let base64 = typeof Buffer !== "undefined" ? Buffer.from(jsonString).toString("base64") : btoa(jsonString);
950
957
  return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;
951
958
  }
@@ -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.\r\n */\r\n async checkStatus(): 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(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/FEDummy',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.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 * 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 * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista (URL-safe encoding para evitar que caracteres como + rompan la validación)\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\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"]}
package/dist/index.d.cts CHANGED
@@ -274,6 +274,11 @@ declare class WsfeService {
274
274
  concepto?: Concepto;
275
275
  fecha?: Date;
276
276
  }): Promise<CAEResponse>;
277
+ /**
278
+ * Verifica el estado de los servidores de ARCA (FEDummy)
279
+ * No requiere autenticación ni instancia configurada.
280
+ */
281
+ static checkStatus(environment?: 'homologacion' | 'produccion'): Promise<ServiceStatus>;
277
282
  /**
278
283
  * Verifica el estado de los servidores de ARCA (FEDummy)
279
284
  * No requiere autenticación.
@@ -346,12 +351,13 @@ declare class WsfeService {
346
351
  }
347
352
 
348
353
  /**
349
- * Genera la URL completa con el código QR para un comprobante emitido
354
+ * Genera la URL completa con el código QR para un comprobante emitido.
355
+ * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.
350
356
  *
351
357
  * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)
352
- * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)
358
+ * @param cuitEmisor Tu CUIT (con o sin guiones)
353
359
  * @param total Importe total del comprobante
354
- * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)
360
+ * @param comprador Datos del comprador (opcional)
355
361
  * @returns La URL lista para embeber en un generador de QR
356
362
  */
357
363
  declare function generarUrlQR(caeResponse: CAEResponse, cuitEmisor: string, total: number, comprador?: Comprador): string;
package/dist/index.d.ts CHANGED
@@ -274,6 +274,11 @@ declare class WsfeService {
274
274
  concepto?: Concepto;
275
275
  fecha?: Date;
276
276
  }): Promise<CAEResponse>;
277
+ /**
278
+ * Verifica el estado de los servidores de ARCA (FEDummy)
279
+ * No requiere autenticación ni instancia configurada.
280
+ */
281
+ static checkStatus(environment?: 'homologacion' | 'produccion'): Promise<ServiceStatus>;
277
282
  /**
278
283
  * Verifica el estado de los servidores de ARCA (FEDummy)
279
284
  * No requiere autenticación.
@@ -346,12 +351,13 @@ declare class WsfeService {
346
351
  }
347
352
 
348
353
  /**
349
- * Genera la URL completa con el código QR para un comprobante emitido
354
+ * Genera la URL completa con el código QR para un comprobante emitido.
355
+ * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.
350
356
  *
351
357
  * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)
352
- * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)
358
+ * @param cuitEmisor Tu CUIT (con o sin guiones)
353
359
  * @param total Importe total del comprobante
354
- * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)
360
+ * @param comprador Datos del comprador (opcional)
355
361
  * @returns La URL lista para embeber en un generador de QR
356
362
  */
357
363
  declare function generarUrlQR(caeResponse: CAEResponse, cuitEmisor: string, total: number, comprador?: Comprador): string;
package/dist/index.js CHANGED
@@ -473,7 +473,7 @@ function redondear(valor) {
473
473
  }
474
474
 
475
475
  // src/services/wsfe.ts
476
- var WsfeService = class {
476
+ var WsfeService = class _WsfeService {
477
477
  config;
478
478
  constructor(config) {
479
479
  this.validateConfig(config);
@@ -511,9 +511,9 @@ var WsfeService = class {
511
511
  }
512
512
  /**
513
513
  * Verifica el estado de los servidores de ARCA (FEDummy)
514
- * No requiere autenticación.
514
+ * No requiere autenticación ni instancia configurada.
515
515
  */
516
- async checkStatus() {
516
+ static async checkStatus(environment = "homologacion") {
517
517
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
518
518
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
519
519
  xmlns:ar="http://ar.gov.afip.dif.FEV1/">
@@ -522,7 +522,7 @@ var WsfeService = class {
522
522
  <ar:FEDummy/>
523
523
  </soapenv:Body>
524
524
  </soapenv:Envelope>`;
525
- const endpoint = getWsfeEndpoint(this.config.environment);
525
+ const endpoint = getWsfeEndpoint(environment);
526
526
  const response = await callArcaApi(endpoint, {
527
527
  method: "POST",
528
528
  headers: {
@@ -530,7 +530,7 @@ var WsfeService = class {
530
530
  "SOAPAction": "http://ar.gov.afip.dif.FEV1/FEDummy"
531
531
  },
532
532
  body: soapRequest,
533
- timeout: this.config.timeout || 1e4
533
+ timeout: 1e4
534
534
  });
535
535
  if (!response.ok) {
536
536
  throw new ArcaError(`Error HTTP al consultar estado: ${response.status}`, "HTTP_ERROR");
@@ -547,6 +547,13 @@ var WsfeService = class {
547
547
  AuthServer: data.AuthServer
548
548
  };
549
549
  }
550
+ /**
551
+ * Verifica el estado de los servidores de ARCA (FEDummy)
552
+ * No requiere autenticación.
553
+ */
554
+ async checkStatus() {
555
+ return _WsfeService.checkStatus(this.config.environment);
556
+ }
550
557
  /**
551
558
  * Emite un Ticket C completo (con detalle de items)
552
559
  * Los items no se envían a ARCA, pero se retornan en la respuesta.
@@ -878,30 +885,30 @@ var WsfeService = class {
878
885
 
879
886
  // src/utils/qr.ts
880
887
  function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
888
+ const cleanCuit = cuitEmisor.replace(/\D/g, "");
889
+ const cleanCae = caeResponse.cae.replace(/\D/g, "");
881
890
  const fDate = caeResponse.fecha;
882
891
  const fechaFormat = fDate.length === 8 ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}` : fDate;
883
892
  const docTipo = comprador?.tipoDocumento || 99 /* CONSUMIDOR_FINAL */;
884
- const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;
885
- const qrData = {
893
+ const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\D/g, "") : "0";
894
+ const qrObj = {
886
895
  ver: 1,
887
- // Versión estándar exigida por AFIP
888
896
  fecha: fechaFormat,
889
- cuit: parseInt(cuitEmisor, 10),
890
- ptoVta: caeResponse.puntoVenta,
891
- tipoCmp: caeResponse.tipoComprobante,
892
- nroCmp: caeResponse.nroComprobante,
893
- importe: parseFloat(total.toFixed(2)),
897
+ cuit: Number(cleanCuit),
898
+ ptoVta: Number(caeResponse.puntoVenta),
899
+ tipoCmp: Number(caeResponse.tipoComprobante),
900
+ nroCmp: Number(caeResponse.nroComprobante),
901
+ importe: Number(parseFloat(total.toFixed(2))),
894
902
  moneda: "PES",
895
- // Por defecto PES (Pesos Argentinos)
896
- ctz: 1,
897
- // Cotización (siempre 1 para PES)
898
- tipoDocRec: docTipo,
899
- nroDocRec: docNro,
900
- tipoCodAut: "E",
901
- // 'E' para comprobantes electrónicos
902
- codAut: parseInt(caeResponse.cae, 10)
903
+ ctz: 1
903
904
  };
904
- const jsonString = JSON.stringify(qrData);
905
+ if (docTipo !== 99 /* CONSUMIDOR_FINAL */ || Number(docNro) > 0) {
906
+ qrObj.tipoDocRec = Number(docTipo);
907
+ qrObj.nroDocRec = Number(docNro);
908
+ }
909
+ qrObj.tipoCodAut = "E";
910
+ qrObj.codAut = Number(cleanCae);
911
+ const jsonString = JSON.stringify(qrObj);
905
912
  let base64 = typeof Buffer !== "undefined" ? Buffer.from(jsonString).toString("base64") : btoa(jsonString);
906
913
  return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;
907
914
  }
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.\r\n */\r\n async checkStatus(): 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(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/FEDummy',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.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 * 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 * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista (URL-safe encoding para evitar que caracteres como + rompan la validación)\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 * 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arca-sdk",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "SDK moderna en TypeScript para ARCA (ex-AFIP)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",