arca-sdk 0.2.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -37,6 +37,23 @@ declare class ArcaValidationError extends ArcaError {
37
37
  constructor(message: string, details?: unknown);
38
38
  }
39
39
 
40
+ /**
41
+ * Interface for token persistence.
42
+ * Allows the SDK to automatically handle TA (Ticket de Acceso) lifecycle
43
+ * by delegating storage to an external system (DB, Cache, etc).
44
+ */
45
+ interface TokenStorage {
46
+ /**
47
+ * Retrieves a stored ticket for a specific CUIT and environment.
48
+ * Should return null if not found or expired.
49
+ */
50
+ get(cuit: string, env: string): Promise<LoginTicket | null>;
51
+ /**
52
+ * Persists a new ticket.
53
+ */
54
+ save(cuit: string, env: string, ticket: LoginTicket): Promise<void>;
55
+ }
56
+
40
57
  /**
41
58
  * Configuración para WSAA
42
59
  */
@@ -47,6 +64,8 @@ interface WsaaConfig extends ArcaConfig {
47
64
  key: string;
48
65
  /** Servicio ARCA a autenticar (ej: 'wsfe', 'wsmtxca') */
49
66
  service: string;
67
+ /** Adaptador opcional para persistencia de tokens */
68
+ storage?: TokenStorage;
50
69
  }
51
70
  /**
52
71
  * Ticket de acceso obtenido de WSAA
@@ -88,8 +107,12 @@ declare class WsaaService {
88
107
  */
89
108
  private validateConfig;
90
109
  /**
91
- * Obtiene un ticket de acceso válido
92
- * Usa cache si el ticket actual es válido
110
+ * Obtiene un ticket de acceso válido.
111
+ *
112
+ * Prioridad de búsqueda:
113
+ * 1. Memoria (TicketManager cache)
114
+ * 2. Persistencia (si config.storage está definido)
115
+ * 3. Nueva solicitud a WSAA
93
116
  *
94
117
  * @returns Ticket de acceso
95
118
  */
@@ -350,6 +373,85 @@ declare class WsfeService {
350
373
  private parseCAEResponse;
351
374
  }
352
375
 
376
+ /**
377
+ * Configuración para PadronService
378
+ */
379
+ interface PadronConfig extends ArcaConfig {
380
+ /** CUIT del emisor (quien consulta) */
381
+ cuit: string;
382
+ /** Certificado X.509 en formato PEM */
383
+ cert: string;
384
+ /** Clave privada en formato PEM */
385
+ key: string;
386
+ /** Adaptador opcional para persistencia de tokens */
387
+ storage?: TokenStorage;
388
+ }
389
+ /**
390
+ * Representa una persona (física o jurídica) obtenida del Padrón A13
391
+ */
392
+ interface Persona {
393
+ idPersona: number;
394
+ tipoPersona: string;
395
+ nombre?: string;
396
+ apellido?: string;
397
+ razonSocial?: string;
398
+ estadoClave: string;
399
+ domicilio: Domicilio[];
400
+ descripcionActividadPrincipal?: string;
401
+ monotributo?: {
402
+ actividad?: string[];
403
+ categoria?: string;
404
+ impuestos?: number[];
405
+ };
406
+ regimenGeneral?: {
407
+ impuestos?: number[];
408
+ actividades?: string[];
409
+ };
410
+ esInscriptoIVA: boolean;
411
+ esMonotributista: boolean;
412
+ esExento: boolean;
413
+ }
414
+ /**
415
+ * Domicilio registrado en AFIP
416
+ */
417
+ interface Domicilio {
418
+ direccion: string;
419
+ localidad?: string;
420
+ codPostal?: string;
421
+ idProvincia: number;
422
+ descripcionProvincia: string;
423
+ tipoDomicilio: string;
424
+ }
425
+ /**
426
+ * Respuesta del servicio de Padrón
427
+ */
428
+ interface PadronResponse {
429
+ persona?: Persona;
430
+ error?: string;
431
+ }
432
+
433
+ /**
434
+ * Servicio para consultar el Padrón de AFIP (ws_sr_padron_a13)
435
+ */
436
+ declare class PadronService {
437
+ private wsaa;
438
+ private config;
439
+ constructor(config: PadronConfig);
440
+ /**
441
+ * Consulta los datos de una persona por CUIT
442
+ *
443
+ * @param idPersona CUIT a consultar
444
+ * @returns Datos de la persona o error
445
+ */
446
+ getPersona(idPersona: string): Promise<PadronResponse>;
447
+ /**
448
+ * Parsear la respuesta XML de getPersona
449
+ */
450
+ private parseResponse;
451
+ private mapDomicilios;
452
+ private checkImpuesto;
453
+ }
454
+
353
455
  /**
354
456
  * Genera la URL completa con el código QR para un comprobante emitido.
355
457
  * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.
@@ -362,4 +464,4 @@ declare class WsfeService {
362
464
  */
363
465
  declare function generarUrlQR(caeResponse: CAEResponse, cuitEmisor: string, total: number, comprador?: Comprador): string;
364
466
 
365
- export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, TipoComprobante, TipoDocumento, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
467
+ export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type Domicilio, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, type PadronConfig, type PadronResponse, PadronService, type Persona, TipoComprobante, TipoDocumento, type TokenStorage, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,23 @@ declare class ArcaValidationError extends ArcaError {
37
37
  constructor(message: string, details?: unknown);
38
38
  }
39
39
 
40
+ /**
41
+ * Interface for token persistence.
42
+ * Allows the SDK to automatically handle TA (Ticket de Acceso) lifecycle
43
+ * by delegating storage to an external system (DB, Cache, etc).
44
+ */
45
+ interface TokenStorage {
46
+ /**
47
+ * Retrieves a stored ticket for a specific CUIT and environment.
48
+ * Should return null if not found or expired.
49
+ */
50
+ get(cuit: string, env: string): Promise<LoginTicket | null>;
51
+ /**
52
+ * Persists a new ticket.
53
+ */
54
+ save(cuit: string, env: string, ticket: LoginTicket): Promise<void>;
55
+ }
56
+
40
57
  /**
41
58
  * Configuración para WSAA
42
59
  */
@@ -47,6 +64,8 @@ interface WsaaConfig extends ArcaConfig {
47
64
  key: string;
48
65
  /** Servicio ARCA a autenticar (ej: 'wsfe', 'wsmtxca') */
49
66
  service: string;
67
+ /** Adaptador opcional para persistencia de tokens */
68
+ storage?: TokenStorage;
50
69
  }
51
70
  /**
52
71
  * Ticket de acceso obtenido de WSAA
@@ -88,8 +107,12 @@ declare class WsaaService {
88
107
  */
89
108
  private validateConfig;
90
109
  /**
91
- * Obtiene un ticket de acceso válido
92
- * Usa cache si el ticket actual es válido
110
+ * Obtiene un ticket de acceso válido.
111
+ *
112
+ * Prioridad de búsqueda:
113
+ * 1. Memoria (TicketManager cache)
114
+ * 2. Persistencia (si config.storage está definido)
115
+ * 3. Nueva solicitud a WSAA
93
116
  *
94
117
  * @returns Ticket de acceso
95
118
  */
@@ -350,6 +373,85 @@ declare class WsfeService {
350
373
  private parseCAEResponse;
351
374
  }
352
375
 
376
+ /**
377
+ * Configuración para PadronService
378
+ */
379
+ interface PadronConfig extends ArcaConfig {
380
+ /** CUIT del emisor (quien consulta) */
381
+ cuit: string;
382
+ /** Certificado X.509 en formato PEM */
383
+ cert: string;
384
+ /** Clave privada en formato PEM */
385
+ key: string;
386
+ /** Adaptador opcional para persistencia de tokens */
387
+ storage?: TokenStorage;
388
+ }
389
+ /**
390
+ * Representa una persona (física o jurídica) obtenida del Padrón A13
391
+ */
392
+ interface Persona {
393
+ idPersona: number;
394
+ tipoPersona: string;
395
+ nombre?: string;
396
+ apellido?: string;
397
+ razonSocial?: string;
398
+ estadoClave: string;
399
+ domicilio: Domicilio[];
400
+ descripcionActividadPrincipal?: string;
401
+ monotributo?: {
402
+ actividad?: string[];
403
+ categoria?: string;
404
+ impuestos?: number[];
405
+ };
406
+ regimenGeneral?: {
407
+ impuestos?: number[];
408
+ actividades?: string[];
409
+ };
410
+ esInscriptoIVA: boolean;
411
+ esMonotributista: boolean;
412
+ esExento: boolean;
413
+ }
414
+ /**
415
+ * Domicilio registrado en AFIP
416
+ */
417
+ interface Domicilio {
418
+ direccion: string;
419
+ localidad?: string;
420
+ codPostal?: string;
421
+ idProvincia: number;
422
+ descripcionProvincia: string;
423
+ tipoDomicilio: string;
424
+ }
425
+ /**
426
+ * Respuesta del servicio de Padrón
427
+ */
428
+ interface PadronResponse {
429
+ persona?: Persona;
430
+ error?: string;
431
+ }
432
+
433
+ /**
434
+ * Servicio para consultar el Padrón de AFIP (ws_sr_padron_a13)
435
+ */
436
+ declare class PadronService {
437
+ private wsaa;
438
+ private config;
439
+ constructor(config: PadronConfig);
440
+ /**
441
+ * Consulta los datos de una persona por CUIT
442
+ *
443
+ * @param idPersona CUIT a consultar
444
+ * @returns Datos de la persona o error
445
+ */
446
+ getPersona(idPersona: string): Promise<PadronResponse>;
447
+ /**
448
+ * Parsear la respuesta XML de getPersona
449
+ */
450
+ private parseResponse;
451
+ private mapDomicilios;
452
+ private checkImpuesto;
453
+ }
454
+
353
455
  /**
354
456
  * Genera la URL completa con el código QR para un comprobante emitido.
355
457
  * Implementación robusta (Versión 11) que limpia inputs y asegura compatibilidad total con AFIP.
@@ -362,4 +464,4 @@ declare class WsfeService {
362
464
  */
363
465
  declare function generarUrlQR(caeResponse: CAEResponse, cuitEmisor: string, total: number, comprador?: Comprador): string;
364
466
 
365
- export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, TipoComprobante, TipoDocumento, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
467
+ export { ArcaAuthError, type ArcaConfig, ArcaError, ArcaValidationError, type CAEResponse, type Comprador, Concepto, type Domicilio, type EmitirFacturaRequest, type Environment, type FacturaItem, type LoginTicket, type PadronConfig, type PadronResponse, PadronService, type Persona, TipoComprobante, TipoDocumento, type TokenStorage, type WsaaConfig, WsaaService, type WsfeConfig, WsfeService, generarUrlQR };
package/dist/index.js CHANGED
@@ -7,12 +7,19 @@ var WSFE_ENDPOINTS = {
7
7
  homologacion: "https://wswhomo.afip.gov.ar/wsfev1/service.asmx",
8
8
  produccion: "https://servicios1.afip.gov.ar/wsfev1/service.asmx"
9
9
  };
10
+ var PADRON_A13_ENDPOINTS = {
11
+ homologacion: "https://awshomo.afip.gov.ar/sr-padron/webservices/personaServiceA13",
12
+ produccion: "https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA13"
13
+ };
10
14
  function getWsaaEndpoint(environment) {
11
15
  return WSAA_ENDPOINTS[environment];
12
16
  }
13
17
  function getWsfeEndpoint(environment) {
14
18
  return WSFE_ENDPOINTS[environment];
15
19
  }
20
+ function getPadronEndpoint(environment) {
21
+ return PADRON_A13_ENDPOINTS[environment];
22
+ }
16
23
 
17
24
  // src/types/common.ts
18
25
  var ArcaError = class extends Error {
@@ -338,8 +345,12 @@ var WsaaService = class {
338
345
  }
339
346
  }
340
347
  /**
341
- * Obtiene un ticket de acceso válido
342
- * Usa cache si el ticket actual es válido
348
+ * Obtiene un ticket de acceso válido.
349
+ *
350
+ * Prioridad de búsqueda:
351
+ * 1. Memoria (TicketManager cache)
352
+ * 2. Persistencia (si config.storage está definido)
353
+ * 3. Nueva solicitud a WSAA
343
354
  *
344
355
  * @returns Ticket de acceso
345
356
  */
@@ -348,8 +359,29 @@ var WsaaService = class {
348
359
  if (cachedTicket) {
349
360
  return cachedTicket;
350
361
  }
362
+ if (this.config.storage) {
363
+ try {
364
+ const storedTicket = await this.config.storage.get(this.config.cuit, this.config.environment);
365
+ if (storedTicket) {
366
+ const now = /* @__PURE__ */ new Date();
367
+ if (new Date(storedTicket.expirationTime) > new Date(now.getTime() + 5 * 6e4)) {
368
+ this.ticketManager.setTicket(storedTicket);
369
+ return storedTicket;
370
+ }
371
+ }
372
+ } catch (error) {
373
+ console.warn("[ARCA-SDK] TokenStorage.get fall\xF3, intentando login directo:", error);
374
+ }
375
+ }
351
376
  const ticket = await this.requestNewTicket();
352
377
  this.ticketManager.setTicket(ticket);
378
+ if (this.config.storage) {
379
+ try {
380
+ await this.config.storage.save(this.config.cuit, this.config.environment, ticket);
381
+ } catch (error) {
382
+ console.warn("[ARCA-SDK] TokenStorage.save fall\xF3:", error);
383
+ }
384
+ }
353
385
  return ticket;
354
386
  }
355
387
  /**
@@ -883,6 +915,129 @@ var WsfeService = class _WsfeService {
883
915
  }
884
916
  };
885
917
 
918
+ // src/services/padron.ts
919
+ import { XMLParser as XMLParser2 } from "fast-xml-parser";
920
+ var PadronService = class {
921
+ wsaa;
922
+ config;
923
+ constructor(config) {
924
+ this.config = config;
925
+ this.wsaa = new WsaaService({
926
+ environment: config.environment,
927
+ cuit: config.cuit,
928
+ cert: config.cert,
929
+ key: config.key,
930
+ service: "ws_sr_padron_a13",
931
+ storage: config.storage
932
+ });
933
+ }
934
+ /**
935
+ * Consulta los datos de una persona por CUIT
936
+ *
937
+ * @param idPersona CUIT a consultar
938
+ * @returns Datos de la persona o error
939
+ */
940
+ async getPersona(idPersona) {
941
+ const ticket = await this.wsaa.login();
942
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
943
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
944
+ xmlns:a13="http://a13.soap.ws.server.puc.sr/">
945
+ <soapenv:Header/>
946
+ <soapenv:Body>
947
+ <a13:getPersona>
948
+ <token>${ticket.token}</token>
949
+ <sign>${ticket.sign}</sign>
950
+ <cuitRepresentada>${this.config.cuit}</cuitRepresentada>
951
+ <idPersona>${idPersona}</idPersona>
952
+ </a13:getPersona>
953
+ </soapenv:Body>
954
+ </soapenv:Envelope>`;
955
+ const endpoint = getPadronEndpoint(this.config.environment);
956
+ const response = await callArcaApi(endpoint, {
957
+ method: "POST",
958
+ headers: {
959
+ "Content-Type": "text/xml; charset=utf-8",
960
+ "SOAPAction": ""
961
+ // A13 no suele requerir SOAPAction específica en el header
962
+ },
963
+ body: soapRequest,
964
+ timeout: this.config.timeout || 15e3
965
+ });
966
+ if (!response.ok) {
967
+ throw new ArcaNetworkError(
968
+ `Error HTTP al comunicarse con Padron A13: ${response.status}`,
969
+ { status: response.status }
970
+ );
971
+ }
972
+ const xml = await response.text();
973
+ return this.parseResponse(xml);
974
+ }
975
+ /**
976
+ * Parsear la respuesta XML de getPersona
977
+ */
978
+ parseResponse(xml) {
979
+ const parser = new XMLParser2({
980
+ ignoreAttributes: false,
981
+ removeNSPrefix: true
982
+ });
983
+ const result = parser.parse(xml);
984
+ const body = result.Envelope?.Body;
985
+ if (!body) {
986
+ throw new ArcaError("Respuesta de Padr\xF3n inv\xE1lida: No se encontr\xF3 Body", "PADRON_ERROR");
987
+ }
988
+ const response = body.getPersonaResponse?.personaReturn;
989
+ if (!response) {
990
+ const fault = body.Fault;
991
+ if (fault) {
992
+ return { error: fault.faultstring || "Error desconocido en AFIP" };
993
+ }
994
+ return { error: "No se encontraron datos para el CUIT informado" };
995
+ }
996
+ if (response.errorConstancia) {
997
+ return { error: response.errorConstancia };
998
+ }
999
+ const p = response.persona;
1000
+ if (!p) {
1001
+ return { error: "CUIT no encontrado" };
1002
+ }
1003
+ const persona = {
1004
+ idPersona: Number(p.idPersona),
1005
+ tipoPersona: p.tipoPersona,
1006
+ nombre: p.nombre,
1007
+ apellido: p.apellido,
1008
+ razonSocial: p.razonSocial,
1009
+ estadoClave: p.estadoClave,
1010
+ domicilio: this.mapDomicilios(p.domicilio),
1011
+ descripcionActividadPrincipal: p.descripcionActividadPrincipal,
1012
+ esInscriptoIVA: this.checkImpuesto(p, 30),
1013
+ // 30 = IVA
1014
+ esMonotributista: this.checkImpuesto(p, 20),
1015
+ // 20 = Monotributo
1016
+ esExento: this.checkImpuesto(p, 32)
1017
+ // 32 = Exento
1018
+ };
1019
+ return { persona };
1020
+ }
1021
+ mapDomicilios(d) {
1022
+ if (!d) return [];
1023
+ const list = Array.isArray(d) ? d : [d];
1024
+ return list.map((item) => ({
1025
+ direccion: item.direccion,
1026
+ localidad: item.localidad,
1027
+ codPostal: item.codPostal,
1028
+ idProvincia: Number(item.idProvincia),
1029
+ descripcionProvincia: item.descripcionProvincia,
1030
+ tipoDomicilio: item.tipoDomicilio
1031
+ }));
1032
+ }
1033
+ checkImpuesto(p, id) {
1034
+ const impuestos = p.impuesto;
1035
+ if (!impuestos) return false;
1036
+ const list = Array.isArray(impuestos) ? impuestos : [impuestos];
1037
+ return list.some((i) => Number(i.idImpuesto) === id);
1038
+ }
1039
+ };
1040
+
886
1041
  // src/utils/qr.ts
887
1042
  function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
888
1043
  const cleanCuit = cuitEmisor.replace(/\D/g, "");
@@ -917,6 +1072,7 @@ export {
917
1072
  ArcaError,
918
1073
  ArcaValidationError,
919
1074
  Concepto,
1075
+ PadronService,
920
1076
  TipoComprobante,
921
1077
  TipoDocumento,
922
1078
  WsaaService,