arca-sdk 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -6,7 +6,9 @@ SDK en TypeScript para integración con servicios de ARCA:
6
6
  - ✅ **Type-safe**: TypeScript strict mode
7
7
  - ✅ **Simple**: No más XML manual
8
8
  - ✅ **Automático**: Cache de tokens, retry logic
9
- - ✅ **Moderno**: ESM + CJS, Node.js 18+
9
+ - ✅ **Fiscal**: Generador de QR oficial AFIP integrado
10
+ - ✅ **Resiliente**: Maneja errores SSL ("dh key too small") y timeouts
11
+ - ✅ **Moderno**: ESM + CJS nativo, Node.js 18+
10
12
 
11
13
  ---
12
14
 
@@ -143,6 +145,15 @@ try {
143
145
 
144
146
  Los errores incluyen contexto útil en `error.details`.
145
147
 
148
+ #### Timeouts
149
+ Por defecto, las peticiones tienen un timeout de **15 segundos**. Podés ajustarlo en la configuración:
150
+ ```typescript
151
+ const wsfe = new WsfeService({
152
+ ...config,
153
+ timeout: 30000 // 30 segundos
154
+ });
155
+ ```
156
+
146
157
  ---
147
158
 
148
159
  ### WSFE - Facturación Electrónica
@@ -222,6 +233,38 @@ const cae = await wsfe.emitirFacturaA({
222
233
  });
223
234
  ```
224
235
 
236
+ #### Precios con IVA Incluido
237
+ Si tus precios ya tienen el IVA (típico en venta minorista/POS), podés usar el flag `incluyeIva`:
238
+ ```typescript
239
+ const cae = await wsfe.emitirFacturaB({
240
+ incluyeIva: true, // ← El SDK calculará el neto y el IVA automáticamente
241
+ items: [
242
+ { descripcion: 'Producto', cantidad: 1, precioUnitario: 1210, alicuotaIva: 21 },
243
+ ],
244
+ // ... comprador
245
+ });
246
+ // Internamente enviará: Subtotal: 1000, IVA: 210, Total: 1210
247
+ ```
248
+
249
+ ---
250
+
251
+ ## 📱 Generador de QR Oficial
252
+ AFIP exige que los comprobantes impresos tengan un código QR con los datos fiscales. La SDK lo genera por vos:
253
+
254
+ ```typescript
255
+ import { generarUrlQR } from 'arca-sdk';
256
+
257
+ // Usá la respuesta del CAE para generar la URL del QR
258
+ const urlQr = generarUrlQR(cae);
259
+
260
+ console.log('URL para QR:', urlQr);
261
+ // Output: https://www.afip.gob.ar/fe/qr/?p=eyJ2ZXIiOjEsImZlY2hh...
262
+ ```
263
+
264
+ Esta URL la podés pasar a cualquier librería de generación de imágenes QR.
265
+
266
+ ---
267
+
225
268
  ---
226
269
 
227
270
  ## 🎯 Tipos de Comprobante
package/dist/index.cjs CHANGED
@@ -253,42 +253,70 @@ var import_https = __toESM(require("https"), 1);
253
253
  async function callArcaApi(url, options) {
254
254
  const timeout = options.timeout || 15e3;
255
255
  const isNode = typeof process !== "undefined" && process.versions && process.versions.node;
256
- const controller = new AbortController();
257
- const timeoutId = setTimeout(() => controller.abort(), timeout);
258
- try {
259
- if (isNode) {
256
+ if (isNode) {
257
+ return new Promise((resolve, reject) => {
258
+ const parsedUrl = new URL(url);
260
259
  const agent = new import_https.default.Agent({
261
- // Permitir llaves DH de 1024 bits (AFIP) bajando a Security Level 1
262
- // solo para esta conexión específica.
263
260
  ciphers: "DEFAULT@SECLEVEL=1",
264
261
  rejectUnauthorized: true
265
262
  });
266
- return await fetch(url, {
263
+ const reqOptions = {
267
264
  method: options.method,
265
+ hostname: parsedUrl.hostname,
266
+ path: parsedUrl.pathname + parsedUrl.search,
268
267
  headers: options.headers,
269
- body: options.body,
270
268
  agent,
271
- signal: controller.signal
269
+ timeout
270
+ };
271
+ const req = import_https.default.request(reqOptions, (res) => {
272
+ let data = "";
273
+ res.on("data", (chunk) => {
274
+ data += chunk;
275
+ });
276
+ res.on("end", () => {
277
+ const response = {
278
+ ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,
279
+ status: res.statusCode || 0,
280
+ statusText: res.statusMessage || "",
281
+ text: async () => data,
282
+ json: async () => JSON.parse(data)
283
+ };
284
+ resolve(response);
285
+ });
272
286
  });
273
- }
274
- return await fetch(url, {
287
+ req.on("error", (error) => {
288
+ let message = error.message;
289
+ if (message.includes("dh key too small")) {
290
+ message = "Error SSL de ARCA (DH Key too small). Verifique su versi\xF3n de OpenSSL/Node.";
291
+ }
292
+ reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {
293
+ url,
294
+ originalError: error
295
+ }));
296
+ });
297
+ req.on("timeout", () => {
298
+ req.destroy();
299
+ reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));
300
+ });
301
+ req.write(options.body);
302
+ req.end();
303
+ });
304
+ }
305
+ const controller = new AbortController();
306
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
307
+ try {
308
+ const response = await fetch(url, {
275
309
  method: options.method,
276
310
  headers: options.headers,
277
311
  body: options.body,
278
312
  signal: controller.signal
279
313
  });
314
+ return response;
280
315
  } catch (error) {
281
316
  if (error.name === "AbortError") {
282
317
  throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);
283
318
  }
284
- let message = error.message;
285
- if (message.includes("dh key too small")) {
286
- message = "Error SSL de ARCA (DH Key too small). El SDK intent\xF3 mitigarlo pero fall\xF3. Verifique su versi\xF3n de Node.js.";
287
- }
288
- throw new ArcaNetworkError(`Error de red al comunicarse con ARCA: ${message}`, {
289
- url,
290
- originalError: error
291
- });
319
+ throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });
292
320
  } finally {
293
321
  clearTimeout(timeoutId);
294
322
  }
@@ -873,3 +901,4 @@ function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
873
901
  WsfeService,
874
902
  generarUrlQR
875
903
  });
904
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["/**\r\n * arca-sdk - SDK moderna para ARCA (ex-AFIP)\r\n */\r\n\r\n// Servicios principales\r\nexport { WsaaService } from './auth/wsaa';\r\nexport { WsfeService } from './services/wsfe';\r\n\r\n// Tipos comunes\r\nexport type {\r\n Environment,\r\n ArcaConfig,\r\n} from './types/common';\r\n\r\n// Tipos WSAA\r\nexport type {\r\n WsaaConfig,\r\n LoginTicket,\r\n} from './types/wsaa';\r\n\r\n// Tipos WSFE\r\nexport type {\r\n WsfeConfig,\r\n FacturaItem,\r\n Comprador,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n} from './types/wsfe';\r\n\r\n// Enums WSFE\r\nexport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento,\r\n} from './types/wsfe';\r\n\r\n// Errores\r\nexport {\r\n ArcaError,\r\n ArcaAuthError,\r\n ArcaValidationError,\r\n} from './types/common';\r\n\r\n// Utilidades útiles para Frontend/Impresión\r\nexport { generarUrlQR } from './utils/qr';\r\n","import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n { receivedStructure: result }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n { originalError: error }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000;\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n if (isNode) {\r\n return new Promise((resolve, reject) => {\r\n const parsedUrl = new URL(url);\r\n\r\n const agent = new https.Agent({\r\n ciphers: 'DEFAULT@SECLEVEL=1',\r\n rejectUnauthorized: true,\r\n });\r\n\r\n const reqOptions: https.RequestOptions = {\r\n method: options.method,\r\n hostname: parsedUrl.hostname,\r\n path: parsedUrl.pathname + parsedUrl.search,\r\n headers: options.headers,\r\n agent,\r\n timeout,\r\n };\r\n\r\n const req = https.request(reqOptions, (res) => {\r\n let data = '';\r\n res.on('data', (chunk) => { data += chunk; });\r\n res.on('end', () => {\r\n // Simular objeto Response de fetch\r\n const response = {\r\n ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,\r\n status: res.statusCode || 0,\r\n statusText: res.statusMessage || '',\r\n text: async () => data,\r\n json: async () => JSON.parse(data),\r\n };\r\n resolve(response as Response);\r\n });\r\n });\r\n\r\n req.on('error', (error: any) => {\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). Verifique su versión de OpenSSL/Node.';\r\n }\r\n reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {\r\n url,\r\n originalError: error\r\n }));\r\n });\r\n\r\n req.on('timeout', () => {\r\n req.destroy();\r\n reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));\r\n });\r\n\r\n req.write(options.body);\r\n req.end();\r\n });\r\n }\r\n\r\n // Navegador o entorno no-Node (utiliza fetch nativo)\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n return response;\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}\r\n","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await fetch(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 });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["https","TipoComprobante","Concepto","TipoDocumento"]}
package/dist/index.js CHANGED
@@ -209,42 +209,70 @@ import https from "https";
209
209
  async function callArcaApi(url, options) {
210
210
  const timeout = options.timeout || 15e3;
211
211
  const isNode = typeof process !== "undefined" && process.versions && process.versions.node;
212
- const controller = new AbortController();
213
- const timeoutId = setTimeout(() => controller.abort(), timeout);
214
- try {
215
- if (isNode) {
212
+ if (isNode) {
213
+ return new Promise((resolve, reject) => {
214
+ const parsedUrl = new URL(url);
216
215
  const agent = new https.Agent({
217
- // Permitir llaves DH de 1024 bits (AFIP) bajando a Security Level 1
218
- // solo para esta conexión específica.
219
216
  ciphers: "DEFAULT@SECLEVEL=1",
220
217
  rejectUnauthorized: true
221
218
  });
222
- return await fetch(url, {
219
+ const reqOptions = {
223
220
  method: options.method,
221
+ hostname: parsedUrl.hostname,
222
+ path: parsedUrl.pathname + parsedUrl.search,
224
223
  headers: options.headers,
225
- body: options.body,
226
224
  agent,
227
- signal: controller.signal
225
+ timeout
226
+ };
227
+ const req = https.request(reqOptions, (res) => {
228
+ let data = "";
229
+ res.on("data", (chunk) => {
230
+ data += chunk;
231
+ });
232
+ res.on("end", () => {
233
+ const response = {
234
+ ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,
235
+ status: res.statusCode || 0,
236
+ statusText: res.statusMessage || "",
237
+ text: async () => data,
238
+ json: async () => JSON.parse(data)
239
+ };
240
+ resolve(response);
241
+ });
228
242
  });
229
- }
230
- return await fetch(url, {
243
+ req.on("error", (error) => {
244
+ let message = error.message;
245
+ if (message.includes("dh key too small")) {
246
+ message = "Error SSL de ARCA (DH Key too small). Verifique su versi\xF3n de OpenSSL/Node.";
247
+ }
248
+ reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {
249
+ url,
250
+ originalError: error
251
+ }));
252
+ });
253
+ req.on("timeout", () => {
254
+ req.destroy();
255
+ reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));
256
+ });
257
+ req.write(options.body);
258
+ req.end();
259
+ });
260
+ }
261
+ const controller = new AbortController();
262
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
263
+ try {
264
+ const response = await fetch(url, {
231
265
  method: options.method,
232
266
  headers: options.headers,
233
267
  body: options.body,
234
268
  signal: controller.signal
235
269
  });
270
+ return response;
236
271
  } catch (error) {
237
272
  if (error.name === "AbortError") {
238
273
  throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);
239
274
  }
240
- let message = error.message;
241
- if (message.includes("dh key too small")) {
242
- message = "Error SSL de ARCA (DH Key too small). El SDK intent\xF3 mitigarlo pero fall\xF3. Verifique su versi\xF3n de Node.js.";
243
- }
244
- throw new ArcaNetworkError(`Error de red al comunicarse con ARCA: ${message}`, {
245
- url,
246
- originalError: error
247
- });
275
+ throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });
248
276
  } finally {
249
277
  clearTimeout(timeoutId);
250
278
  }
@@ -828,3 +856,4 @@ export {
828
856
  WsfeService,
829
857
  generarUrlQR
830
858
  };
859
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n { receivedStructure: result }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n { originalError: error }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000;\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n if (isNode) {\r\n return new Promise((resolve, reject) => {\r\n const parsedUrl = new URL(url);\r\n\r\n const agent = new https.Agent({\r\n ciphers: 'DEFAULT@SECLEVEL=1',\r\n rejectUnauthorized: true,\r\n });\r\n\r\n const reqOptions: https.RequestOptions = {\r\n method: options.method,\r\n hostname: parsedUrl.hostname,\r\n path: parsedUrl.pathname + parsedUrl.search,\r\n headers: options.headers,\r\n agent,\r\n timeout,\r\n };\r\n\r\n const req = https.request(reqOptions, (res) => {\r\n let data = '';\r\n res.on('data', (chunk) => { data += chunk; });\r\n res.on('end', () => {\r\n // Simular objeto Response de fetch\r\n const response = {\r\n ok: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,\r\n status: res.statusCode || 0,\r\n statusText: res.statusMessage || '',\r\n text: async () => data,\r\n json: async () => JSON.parse(data),\r\n };\r\n resolve(response as Response);\r\n });\r\n });\r\n\r\n req.on('error', (error: any) => {\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). Verifique su versión de OpenSSL/Node.';\r\n }\r\n reject(new ArcaNetworkError(`Error de red al comunicarse con ARCA (HTTPS): ${message}`, {\r\n url,\r\n originalError: error\r\n }));\r\n });\r\n\r\n req.on('timeout', () => {\r\n req.destroy();\r\n reject(new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`));\r\n });\r\n\r\n req.write(options.body);\r\n req.end();\r\n });\r\n }\r\n\r\n // Navegador o entorno no-Node (utiliza fetch nativo)\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n return response;\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n throw new ArcaNetworkError(`Error de red: ${error.message}`, { url, originalError: error });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}\r\n","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await fetch(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 });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["TipoComprobante","Concepto","TipoDocumento"]}
package/package.json CHANGED
@@ -1,17 +1,14 @@
1
1
  {
2
2
  "name": "arca-sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "SDK moderna en TypeScript para ARCA (ex-AFIP)",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": {
12
- "import": "./dist/index.d.ts",
13
- "require": "./dist/index.d.cts"
14
- },
11
+ "types": "./dist/index.d.ts",
15
12
  "import": "./dist/index.js",
16
13
  "require": "./dist/index.cjs"
17
14
  }
@@ -23,7 +20,7 @@
23
20
  ],
24
21
  "scripts": {
25
22
  "dev": "tsup src/index.ts --watch",
26
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
23
+ "build": "tsup",
27
24
  "test": "vitest run",
28
25
  "test:watch": "vitest",
29
26
  "test:coverage": "vitest run --coverage",