arca-sdk 1.0.2 → 1.0.4

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/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ Todos los cambios notables de este proyecto se documentan en este archivo.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.0.4] — 2026-02-27
8
+
9
+ ### 🐛 Bugfix — Timezone Handling
10
+
11
+ - Se ajustó la generación de fechas para forzar la zona horaria UTC-3 (Argentina) independientemente de la zona horaria del servidor (ej: AWS, Vercel).
12
+ - Se restan 10 minutos al tiempo de generación en los TRA para evitar errores de desincronización con los servidores de ARCA.
13
+
14
+ ---
15
+
7
16
  ## [1.0.1] — 2026-02-23
8
17
 
9
18
  ### 🐛 Bugfix crítico — QR URL
package/README.md CHANGED
@@ -1,44 +1,97 @@
1
+ <div align="center">
2
+
1
3
  # 🇦🇷 arca-sdk
2
4
 
3
- **La SDK moderna de ARCA (ex-AFIP) que no te rompe las bolas.**
5
+ **La SDK de ARCA (ex-AFIP) que querías que alguien hiciera.**
6
+
7
+ TypeScript nativo · API limpia en inglés · Tokens automáticos · QR oficial · Padrón A13
4
8
 
5
- SDK en TypeScript para integración con servicios de ARCA:
6
- - ✅ **Type-safe**: TypeScript strict mode, 100% tipado
7
- - ✅ **Simple**: API en inglés, sin XML manual, sin magia negra
8
- - ✅ **Automático**: Cache de tokens + persistencia opcional (God Mode)
9
- - ✅ **Fiscal**: Generador de QR oficial de ARCA ultra-robusto
10
- - ✅ **Padrón**: Consulta de CUIT (A13) para autocompletado de datos
11
- - ✅ **Resiliente**: Maneja errores SSL ("dh key too small") y timeouts
12
- - ✅ **Moderno**: ESM + CJS nativo, Node.js 18+, Bun compatible
9
+ [![npm version](https://img.shields.io/npm/v/arca-sdk?color=CB3837&label=npm)](https://www.npmjs.com/package/arca-sdk)
10
+ [![npm downloads](https://img.shields.io/npm/dm/arca-sdk?color=CB3837)](https://www.npmjs.com/package/arca-sdk)
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
12
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
13
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
14
+
15
+ </div>
13
16
 
14
17
  ---
15
18
 
16
- ## 🚀 Instalación
19
+ ## ¿Por qué arca-sdk?
20
+
21
+ La mayoría de las librerías de AFIP/ARCA para Node.js son:
22
+ - Ports de código PHP/Java sin tipos
23
+ - Sin soporte para el Padrón A13
24
+ - Sin manejo de tokens (te obligan a gestionarlos a mano)
25
+ - Sin QR oficial
26
+ - Sin mantenimiento activo
27
+
28
+ `arca-sdk` fue construida desde cero en TypeScript moderno, probada contra producción real, y documenta quirks del spec oficial que ninguna otra librería menciona (como el `encodeURIComponent` que rompe el scanner de ARCA).
29
+
30
+ ---
31
+
32
+ ## Instalación
33
+
17
34
  ```bash
35
+ # npm
18
36
  npm install arca-sdk
19
- # o
37
+
38
+ # bun
20
39
  bun add arca-sdk
40
+
41
+ # pnpm
42
+ pnpm add arca-sdk
21
43
  ```
22
44
 
45
+ **Requisitos:** Node.js 18+ · TypeScript 5+ (optional but recommended) · Bun compatible
46
+
47
+ ---
48
+
49
+ ## 🔐 Security & Responsibility / Seguridad y Responsabilidad
50
+
51
+ ### 🇺🇸 English
52
+
53
+ **arca-sdk** is designed to be **stateless and cloud-native**. It does **NOT** persist certificates or private keys to the filesystem.
54
+
55
+ It is the responsibility of the implementing application to:
56
+ 1. **Securely store** the Private Key (`.key`) and Certificate (`.crt`). (Recommended: Encrypted Database, AWS KMS, HashiCorp Vault).
57
+ 2. **Decrypt** credentials only at runtime.
58
+ 3. Pass the raw strings/buffers to the SDK constructors.
59
+
60
+ > [!WARNING]
61
+ > Never commit your `.key` files to Git or expose them in public folders. The SDK operates in-memory to ensure maximum security in Serverless environments (Vercel, AWS Lambda).
62
+
63
+ ### 🇦🇷 Español
64
+
65
+ **arca-sdk** está diseñado para ser **stateless** (sin estado) y **cloud-native**. **NO** guarda certificados ni claves privadas en el sistema de archivos.
66
+
67
+ Es responsabilidad de la aplicación que implementa el SDK:
68
+ 1. **Almacenar de forma segura** la Clave Privada (`.key`) y el Certificado (`.crt`). (Recomendado: Base de Datos encriptada, AWS KMS, HashiCorp Vault).
69
+ 2. **Desencriptar** las credenciales solo en tiempo de ejecución.
70
+ 3. Pasar los strings o buffers crudos a los constructores del SDK.
71
+
72
+ > [!CAUTION]
73
+ > Nunca subas tus archivos `.key` a Git ni los expongas en carpetas públicas. El SDK opera en memoria para garantizar la máxima seguridad en entornos Serverless (Vercel, AWS Lambda).
74
+
23
75
  ---
24
76
 
25
- ## Quick Start
77
+ ## Quick Start — 5 minutos y estás facturando
78
+
26
79
  ```typescript
27
- import { WsaaService, WsfeService } from 'arca-sdk';
28
80
  import * as fs from 'fs';
81
+ import { WsaaService, WsfeService } from 'arca-sdk';
29
82
 
30
- // 1. Autenticar con WSAA
83
+ // 1. Autenticación con WSAA (se renueva automáticamente)
31
84
  const wsaa = new WsaaService({
32
- environment: 'homologacion',
85
+ environment: 'homologacion', // 'produccion' cuando estés listo
33
86
  cuit: '20123456789',
34
87
  cert: fs.readFileSync('cert.pem', 'utf-8'),
35
- key: fs.readFileSync('key.pem', 'utf-8'),
88
+ key: fs.readFileSync('key.pem', 'utf-8'),
36
89
  service: 'wsfe',
37
90
  });
38
91
 
39
92
  const ticket = await wsaa.login();
40
93
 
41
- // 2. Crear servicio de facturación
94
+ // 2. Servicio de facturación
42
95
  const wsfe = new WsfeService({
43
96
  environment: 'homologacion',
44
97
  cuit: '20123456789',
@@ -46,183 +99,276 @@ const wsfe = new WsfeService({
46
99
  pointOfSale: 4,
47
100
  });
48
101
 
49
- // 3. Emitir un Ticket C
102
+ // 3. Emitir Ticket C — una línea
50
103
  const result = await wsfe.issueSimpleReceipt({ total: 1500 });
51
104
 
52
- console.log('CAE:', result.cae);
53
- console.log('QR URL:', result.qrUrl); // ← Ya viene integrado!
105
+ console.log('CAE:', result.cae); // '75157992335329'
106
+ console.log('Vto:', result.caeExpiry); // '20260302'
107
+ console.log('QR:', result.qrUrl); // 'https://www.afip.gob.ar/fe/qr/?p=...'
54
108
  ```
55
109
 
110
+ > Los certificados se obtienen en el [portal de ARCA](https://auth.afip.gob.ar/contribuyente_/login.xhtml) (CLAVE FISCAL nivel 3+).
111
+
56
112
  ---
57
113
 
58
- ## 👑 God Mode: Persistencia Automática
59
- No manejes tokens manualmente. Pasale un `storage` al SDK y se encargará de guardar, recuperar y renovar el TA solo cuando expire.
114
+ ## Funcionalidades
60
115
 
61
- ```typescript
62
- const wsaa = new WsaaService({
63
- ...config,
64
- service: 'wsfe',
65
- storage: {
66
- get: async (cuit, env) => await db.token.findUnique({ where: { cuit, env } }),
67
- save: async (cuit, env, ticket) => await db.token.upsert({ ... }),
68
- }
69
- });
70
-
71
- // El SDK chequea el storage antes de pedir un nuevo ticket a ARCA
72
- const ticket = await wsaa.login();
73
- ```
116
+ ### ✅ Servicios soportados
74
117
 
75
- ---
118
+ | Servicio | Descripción | Estado |
119
+ |----------|-------------|--------|
120
+ | **WSAA** | Autenticación y Autorización | ✅ Completo |
121
+ | **WSFE v1** | Facturación Electrónica (A, B, C) | ✅ Completo |
122
+ | **Padrón A13** | Consulta de datos de contribuyentes | ✅ Completo |
76
123
 
77
- ## 🔍 Consulta de Padrón (A13)
78
- Obtené los datos de un contribuyente (nombre, domicilio, condición IVA) solo con su CUIT.
124
+ ### Tipos de comprobantes
79
125
 
80
- ```typescript
81
- import { PadronService } from 'arca-sdk';
126
+ | Método | Comprobante | Cuándo usarlo |
127
+ |--------|-------------|---------------|
128
+ | `issueSimpleReceipt()` | Ticket C | Monto total, sin detalle. Ideal para POS simple |
129
+ | `issueReceipt()` | Ticket C + items | Con detalle de productos guardado localmente |
130
+ | `issueInvoiceC()` | Factura C | Monotributistas a consumidor final |
131
+ | `issueInvoiceB()` | Factura B | Responsable Inscripto a consumidor final / Monotributo |
132
+ | `issueInvoiceA()` | Factura A | Responsable Inscripto a Responsable Inscripto |
82
133
 
83
- const padron = new PadronService(config);
84
- const { taxpayer, error } = await padron.getTaxpayer('30111111118');
134
+ ### Consultas disponibles
85
135
 
86
- if (taxpayer) {
87
- const name = taxpayer.companyName || `${taxpayer.firstName} ${taxpayer.lastName}`;
88
- console.log('Nombre:', name);
89
- console.log('Provincia:', taxpayer.addresses[0].province);
90
- console.log('¿Inscripto IVA?:', taxpayer.isVATRegistered);
91
- console.log('¿Monotributista?:', taxpayer.isMonotax);
92
- }
93
- ```
136
+ | Método | Descripción |
137
+ |--------|-------------|
138
+ | `wsfe.getInvoice(type, n)` | Consulta un comprobante ya emitido (FECompConsultar) |
139
+ | `wsfe.getPointsOfSale()` | Lista puntos de venta habilitados (FEParamGetPtosVenta) |
140
+ | `WsfeService.checkStatus()` | Estado de los servidores de ARCA (FEDummy) |
141
+ | `padron.getTaxpayer(cuit)` | Datos del contribuyente — nombre, domicilio, condición IVA |
94
142
 
95
143
  ---
96
144
 
97
- ## 📱 Comprobantes soportados
145
+ ## Ejemplos
98
146
 
99
- ```typescript
100
- // Ticket C — Consumidor Final (solo total)
101
- const cae = await wsfe.issueSimpleReceipt({ total: 1500 });
147
+ ### Ticket C con detalle de items
102
148
 
103
- // Ticket C — con detalle de items (los items se guardan localmente, no van a ARCA)
104
- const cae = await wsfe.issueReceipt({
149
+ ```typescript
150
+ const result = await wsfe.issueReceipt({
105
151
  items: [
106
- { description: 'Café con leche', quantity: 2, unitPrice: 750 },
107
- { description: 'Medialunas x3', quantity: 1, unitPrice: 500 },
152
+ { description: 'Café con leche', quantity: 2, unitPrice: 750 },
153
+ { description: 'Medialunas x4', quantity: 1, unitPrice: 600 },
108
154
  ],
109
155
  });
110
156
 
111
- // Factura C Consumidor Final con items
112
- const cae = await wsfe.issueInvoiceC({ items: [...] });
157
+ console.log('Items en respuesta:', result.items?.length); // 2
158
+ console.log('QR URL:', result.qrUrl);
159
+ ```
160
+
161
+ ### Factura B con IVA discriminado
162
+
163
+ ```typescript
164
+ import { TaxIdType } from 'arca-sdk';
113
165
 
114
- // Factura B con IVA discriminado (requiere vatRate en cada item)
115
- const cae = await wsfe.issueInvoiceB({
166
+ const result = await wsfe.issueInvoiceB({
116
167
  items: [
117
168
  { description: 'Servicio de diseño', quantity: 10, unitPrice: 1000, vatRate: 21 },
169
+ { description: 'Hosting mensual', quantity: 1, unitPrice: 5000, vatRate: 21 },
118
170
  ],
119
- buyer: { docType: TaxIdType.CUIT, docNumber: '20987654321' },
171
+ buyer: {
172
+ docType: TaxIdType.CUIT,
173
+ docNumber: '20987654321',
174
+ },
120
175
  });
121
176
 
122
- // Factura A entre Responsables Inscriptos
123
- const cae = await wsfe.issueInvoiceA({
124
- items: [...],
125
- buyer: { docType: TaxIdType.CUIT, docNumber: '30123456789' },
177
+ // VAT breakdown (required by ARCA for A/B invoices)
178
+ result.vat?.forEach(v => {
179
+ console.log(`IVA ${v.rate}%: base $${v.taxBase} → $${v.amount}`);
126
180
  });
127
181
  ```
128
182
 
129
- ---
130
-
131
- ## 🔎 Consultar un comprobante ya emitido
183
+ ### Consulta de Padrón A13
132
184
 
133
185
  ```typescript
134
- const invoice = await wsfe.getInvoice(InvoiceType.TICKET_C, 42);
135
- console.log('Total:', invoice.total);
136
- console.log('CAE:', invoice.cae);
186
+ import { PadronService } from 'arca-sdk';
187
+
188
+ const padron = new PadronService({
189
+ environment: 'homologacion',
190
+ cuit: '20123456789',
191
+ cert: fs.readFileSync('cert.pem', 'utf-8'),
192
+ key: fs.readFileSync('key.pem', 'utf-8'),
193
+ });
194
+
195
+ const { taxpayer, error } = await padron.getTaxpayer('30111111118');
196
+ if (taxpayer) {
197
+ const name = taxpayer.companyName || `${taxpayer.firstName} ${taxpayer.lastName}`;
198
+ console.log('Nombre:', name);
199
+ console.log('Provincia:', taxpayer.addresses[0]?.province);
200
+ console.log('¿IVA?:', taxpayer.isVATRegistered);
201
+ console.log('¿Mono?:', taxpayer.isMonotax);
202
+ }
137
203
  ```
138
204
 
139
- ---
205
+ ### God Mode: persistencia automática de tokens
140
206
 
141
- ## 🏪 Listar puntos de venta
207
+ Pasale un adaptador `storage` y el SDK gestiona el ciclo de vida del ticket solo — incluyendo renovación automática al expirar.
142
208
 
143
209
  ```typescript
144
- const points = await wsfe.getPointsOfSale();
145
- points.forEach(p => {
146
- console.log(`PdV ${p.number}: ${p.type} — ${p.isBlocked ? '🔴 Bloqueado' : '✅ Activo'}`);
210
+ const wsaa = new WsaaService({
211
+ ...config,
212
+ service: 'wsfe',
213
+ storage: {
214
+ // Leé el ticket guardado (de DB, Redis, filesystem, lo que sea)
215
+ get: async (cuit, env) => {
216
+ const row = await db.token.findUnique({ where: { cuit, env } });
217
+ return row ? { token: row.token, sign: row.sign, ... } : null;
218
+ },
219
+ // Guardá el ticket nuevo
220
+ save: async (cuit, env, ticket) => {
221
+ await db.token.upsert({ ... });
222
+ },
223
+ }
147
224
  });
148
- ```
149
225
 
150
- ---
226
+ // Desde ahora, .login() va a buscar el token en la DB antes de pedirle uno a ARCA
227
+ const ticket = await wsaa.login();
228
+ ```
151
229
 
152
- ## 📱 QR Oficial de ARCA
230
+ ### QR oficial de ARCA
153
231
 
154
232
  ```typescript
155
- // 1. Integrado automáticamente en todos los métodos de emisión
233
+ import { generateQRUrl } from 'arca-sdk';
234
+
235
+ // 1. Ya viene integrado en todos los métodos de emisión:
156
236
  const result = await wsfe.issueSimpleReceipt({ total: 1500 });
157
- console.log(result.qrUrl);
237
+ console.log(result.qrUrl); // listo para embeber en un generador de QR
158
238
 
159
- // 2. O generalo manualmente
160
- import { generateQRUrl } from 'arca-sdk';
161
- const url = generateQRUrl(caeResponse, '20123456789', 1500);
239
+ // 2. O generalo a mano si ya tenés la respuesta:
240
+ const url = generateQRUrl(caeResponse, '20123456789', 1500.00);
162
241
  ```
163
242
 
164
- ---
243
+ > **Nota:** La URL usa base64 crudo sin `encodeURIComponent`. Es un quirk documentado del spec de ARCA — su scanner no acepta caracteres URL-encoded.
244
+
245
+ ### Manejo de errores
165
246
 
166
- ## 🩺 Estado de los servidores
247
+ Todos los errores son instancias tipadas de `ArcaError`, con un campo `hint` que te dice qué hacer:
167
248
 
168
249
  ```typescript
169
- // Sin necesidad de autenticación
170
- const status = await WsfeService.checkStatus('produccion');
171
- console.log('AppServer:', status.appServer); // 'OK'
172
- console.log('DbServer:', status.dbServer);
173
- console.log('AuthServer:', status.authServer);
250
+ import { ArcaError, ArcaAuthError, ArcaValidationError, ArcaNetworkError } from 'arca-sdk';
251
+
252
+ try {
253
+ const result = await wsfe.issueSimpleReceipt({ total: 1500 });
254
+ } catch (error) {
255
+ if (error instanceof ArcaAuthError) {
256
+ // Token expirado, certificado inválido, etc.
257
+ console.error('Auth error:', error.message);
258
+ console.log('Hint:', error.hint); // → "El certificado puede haber expirado..."
259
+ } else if (error instanceof ArcaValidationError) {
260
+ // Datos inválidos antes de llamar a ARCA
261
+ console.error('Validation:', error.message, error.details);
262
+ } else if (error instanceof ArcaNetworkError) {
263
+ // Timeout, error HTTP
264
+ console.error('Network:', error.message);
265
+ } else if (error instanceof ArcaError) {
266
+ // Error semántico de ARCA (código de error en la respuesta SOAP)
267
+ console.error(`ARCA Error [${error.code}]:`, error.message);
268
+ console.log('Hint:', error.hint); // Pista específica por código de error
269
+ }
270
+ }
174
271
  ```
175
272
 
176
273
  ---
177
274
 
178
- ## 📝 Referencia de Servicios
275
+ ## Compatibilidad
179
276
 
180
- | Clase | Servicio ARCA | Descripción |
181
- |-------|---------------|-------------|
182
- | `WsaaService` | `wsaa` | Autenticación y Autorización |
183
- | `WsfeService` | `wsfev1` | Facturación Electrónica (A, B, C) |
184
- | `PadronService` | `ws_sr_padron_a13` | Consulta de datos de contribuyentes |
277
+ | Runtime | Versión mínima | Estado |
278
+ |---------|---------------|--------|
279
+ | Node.js | 18 LTS | Soportado |
280
+ | Node.js | 20 LTS | Soportado |
281
+ | Node.js | 22 LTS | Soportado |
282
+ | Bun | 1.x | ✅ Soportado |
283
+ | Deno | — | ⚠️ Sin probar |
284
+ | Browser | — | ❌ No soportado (requiere `node:https`) |
185
285
 
186
- ### Enums disponibles
286
+ > El SDK maneja automáticamente los errores SSL de los servidores de ARCA ("dh key too small") mediante configuración custom del `https.Agent`.
287
+
288
+ ---
289
+
290
+ ## Tipos exportados
187
291
 
188
292
  ```typescript
293
+ // Servicios
294
+ import { WsaaService, WsfeService, PadronService } from 'arca-sdk';
295
+
296
+ // Enums
189
297
  import { InvoiceType, BillingConcept, TaxIdType } from 'arca-sdk';
190
298
 
191
- InvoiceType.FACTURA_A // 1
192
- InvoiceType.FACTURA_B // 6
193
- InvoiceType.FACTURA_C // 11
194
- InvoiceType.TICKET_C // 83
299
+ // Tipos de configuración
300
+ import type { WsaaConfig, WsfeConfig, TaxpayerServiceConfig } from 'arca-sdk';
301
+
302
+ // Tipos de respuesta
303
+ import type { CAEResponse, InvoiceDetails, PointOfSale, ServiceStatus } from 'arca-sdk';
304
+ import type { TaxpayerResponse, Taxpayer, Address, Activity, TaxRecord } from 'arca-sdk';
305
+
306
+ // Items de factura
307
+ import type { InvoiceItem, Buyer, IssueInvoiceRequest } from 'arca-sdk';
308
+
309
+ // Storage
310
+ import type { TokenStorage, LoginTicket } from 'arca-sdk';
195
311
 
196
- BillingConcept.PRODUCTS // 1
197
- BillingConcept.SERVICES // 2
198
- BillingConcept.PRODUCTS_AND_SERVICES // 3
312
+ // Errores
313
+ import { ArcaError, ArcaAuthError, ArcaValidationError, ArcaNetworkError } from 'arca-sdk';
199
314
 
200
- TaxIdType.CUIT // 80
201
- TaxIdType.DNI // 96
202
- TaxIdType.FINAL_CONSUMER // 99
315
+ // QR
316
+ import { generateQRUrl } from 'arca-sdk';
203
317
  ```
204
318
 
205
319
  ---
206
320
 
207
- ## 🛠️ Desarrollo y Tests
321
+ ## Desarrollo
208
322
 
209
323
  ```bash
210
- # Correr tests (unit + mocks de ARCA)
324
+ # Clonar e instalar
325
+ git clone https://github.com/marcelaborgarello/arca-sdk
326
+ cd arca-sdk
327
+ bun install
328
+
329
+ # Tests
211
330
  bun test
212
331
 
213
- # Verificar tipos TypeScript
332
+ # Verificar tipos
214
333
  bun run lint
215
334
 
216
- # Build para producción (CJS + ESM + .d.ts)
335
+ # Build (CJS + ESM + .d.ts)
217
336
  bun run build
218
337
  ```
219
338
 
339
+ ### Tests disponibles
340
+
341
+ | Suite | Archivo | Qué cubre |
342
+ |-------|---------|-----------|
343
+ | Cálculos | `calculations.test.ts` | IVA, subtotales, totales con y sin IVA incluido |
344
+ | QR | `qr.test.ts` | Generación de URL, limpieza de CUIT/CAE, campo comprador |
345
+ | XML | `xml.test.ts` | Construcción de TRA, parsing WSAA, validación de CUIT |
346
+ | Padrón | `padron.test.ts` | Parsing de respuesta, CUIT not found, condición IVA |
347
+ | WSFE | `wsfe.test.ts` | issueSimpleReceipt, issueReceipt, issueInvoiceB, checkStatus |
348
+
220
349
  ---
221
350
 
222
- ## 📄 Licencia
351
+ ## Roadmap
352
+
353
+ - [ ] Soporte WSMTXCA (Factura de Crédito Electrónica MiPyME)
354
+ - [ ] Soporte WSCT (Turismo)
355
+ - [ ] Método `consultar()` para servicios adicionales del Padrón
356
+ - [ ] Opción de exportar a PDF (recibo y factura)
357
+
358
+ ---
359
+
360
+ ## Licencia
361
+
223
362
  MIT © [Marcela Borgarello](https://github.com/marcelaborgarello)
224
363
 
225
364
  ---
226
365
 
366
+ <div align="center">
367
+
227
368
  **Hecho con ❤️ en Argentina 🇦🇷**
369
+
228
370
  *Porque integrar con ARCA no tiene por qué ser un infierno.*
371
+
372
+ [npm](https://www.npmjs.com/package/arca-sdk) · [GitHub](https://github.com/marcelaborgarello/arca-sdk) · [CHANGELOG](CHANGELOG.md)
373
+
374
+ </div>
package/dist/index.cjs CHANGED
@@ -98,16 +98,33 @@ var ArcaNetworkError = class extends ArcaError {
98
98
 
99
99
  // src/utils/xml.ts
100
100
  var import_fast_xml_parser = require("fast-xml-parser");
101
+
102
+ // src/utils/formatArcaDate.ts
103
+ function formatArcaDate(date) {
104
+ const pad = (n) => n.toString().padStart(2, "0");
105
+ const tzOffset = 3 * 60 * 60 * 1e3;
106
+ const baTime = new Date(date.getTime() - tzOffset);
107
+ const yyyy = baTime.getUTCFullYear();
108
+ const mm = pad(baTime.getUTCMonth() + 1);
109
+ const dd = pad(baTime.getUTCDate());
110
+ const hh = pad(baTime.getUTCHours());
111
+ const min = pad(baTime.getUTCMinutes());
112
+ const ss = pad(baTime.getUTCSeconds());
113
+ return `${yyyy}-${mm}-${dd}T${hh}:${min}:${ss}-03:00`;
114
+ }
115
+
116
+ // src/utils/xml.ts
101
117
  function buildTRA(service, cuit) {
102
118
  const now = /* @__PURE__ */ new Date();
103
- const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1e3);
119
+ const genTime = new Date(now.getTime() - 10 * 60 * 1e3);
120
+ const expTime = new Date(now.getTime() + 12 * 60 * 60 * 1e3);
104
121
  const tra = {
105
122
  loginTicketRequest: {
106
123
  "@_version": "1.0",
107
124
  header: {
108
125
  uniqueId: Math.floor(now.getTime() / 1e3),
109
- generationTime: now.toISOString(),
110
- expirationTime: expirationTime.toISOString()
126
+ generationTime: formatArcaDate(genTime),
127
+ expirationTime: formatArcaDate(expTime)
111
128
  },
112
129
  service
113
130
  }