@lapyme/arca 0.1.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/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/index-vWZOjFDO.d.ts +11 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +1387 -0
- package/dist/index.js.map +1 -0
- package/dist/padron.d.ts +31 -0
- package/dist/padron.js +118 -0
- package/dist/padron.js.map +1 -0
- package/dist/types-DWsYue4B.d.ts +53 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/wsfe.d.ts +124 -0
- package/dist/wsfe.js +340 -0
- package/dist/wsfe.js.map +1 -0
- package/dist/wsmtxca.d.ts +53 -0
- package/dist/wsmtxca.js +247 -0
- package/dist/wsmtxca.js.map +1 -0
- package/package.json +88 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1387 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var ArcaError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
name = "ArcaError";
|
|
5
|
+
constructor(message, code = "ARCA_ERROR", options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var ArcaConfigurationError = class extends ArcaError {
|
|
11
|
+
name = "ArcaConfigurationError";
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, "ARCA_CONFIGURATION_ERROR", options);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var ArcaTransportError = class extends ArcaError {
|
|
17
|
+
name = "ArcaTransportError";
|
|
18
|
+
statusCode;
|
|
19
|
+
responseBody;
|
|
20
|
+
constructor(message, options) {
|
|
21
|
+
super(message, "ARCA_TRANSPORT_ERROR", options);
|
|
22
|
+
this.statusCode = options?.statusCode;
|
|
23
|
+
this.responseBody = options?.responseBody;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var ArcaSoapFaultError = class extends ArcaError {
|
|
27
|
+
name = "ArcaSoapFaultError";
|
|
28
|
+
faultCode;
|
|
29
|
+
detail;
|
|
30
|
+
constructor(message, options) {
|
|
31
|
+
super(message, "ARCA_SOAP_FAULT", options);
|
|
32
|
+
this.faultCode = options?.faultCode;
|
|
33
|
+
this.detail = options?.detail;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var ArcaServiceError = class extends ArcaError {
|
|
37
|
+
name = "ArcaServiceError";
|
|
38
|
+
serviceCode;
|
|
39
|
+
detail;
|
|
40
|
+
constructor(message, options) {
|
|
41
|
+
super(message, "ARCA_SERVICE_ERROR", options);
|
|
42
|
+
this.serviceCode = options?.serviceCode;
|
|
43
|
+
this.detail = options?.detail;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/config.ts
|
|
48
|
+
var ARCA_ENVIRONMENTS = ["production", "test"];
|
|
49
|
+
var ARCA_ENV_VARIABLES = {
|
|
50
|
+
taxId: "ARCA_TAX_ID",
|
|
51
|
+
certificatePem: "ARCA_CERTIFICATE_PEM",
|
|
52
|
+
privateKeyPem: "ARCA_PRIVATE_KEY_PEM",
|
|
53
|
+
environment: "ARCA_ENVIRONMENT",
|
|
54
|
+
wsaaCacheMode: "ARCA_WSAA_CACHE_MODE",
|
|
55
|
+
wsaaCacheDirectory: "ARCA_WSAA_CACHE_DIRECTORY"
|
|
56
|
+
};
|
|
57
|
+
var PRIVATE_KEY_PEM_PREFIXES = [
|
|
58
|
+
"-----BEGIN PRIVATE KEY-----",
|
|
59
|
+
"-----BEGIN RSA PRIVATE KEY-----",
|
|
60
|
+
"-----BEGIN ENCRYPTED PRIVATE KEY-----"
|
|
61
|
+
];
|
|
62
|
+
function resolveArcaEnvironment(production) {
|
|
63
|
+
return production ? "production" : "test";
|
|
64
|
+
}
|
|
65
|
+
function createArcaClientConfigFromEnv(options = {}) {
|
|
66
|
+
const env = options.env ?? process.env;
|
|
67
|
+
const variableNames = {
|
|
68
|
+
...ARCA_ENV_VARIABLES,
|
|
69
|
+
...options.variableNames
|
|
70
|
+
};
|
|
71
|
+
const environmentInput = readEnv(env, variableNames.environment);
|
|
72
|
+
const environmentValue = normalizeEnvironmentValue(environmentInput);
|
|
73
|
+
const cacheModeInput = readEnv(env, variableNames.wsaaCacheMode);
|
|
74
|
+
const cacheModeValue = normalizeCacheMode(cacheModeInput);
|
|
75
|
+
const config = {
|
|
76
|
+
taxId: readEnv(env, variableNames.taxId) ?? "",
|
|
77
|
+
certificatePem: readEnv(env, variableNames.certificatePem) ?? "",
|
|
78
|
+
privateKeyPem: readEnv(env, variableNames.privateKeyPem) ?? "",
|
|
79
|
+
environment: environmentValue ?? environmentInput ?? options.defaultEnvironment ?? "test"
|
|
80
|
+
};
|
|
81
|
+
if (cacheModeValue === "disk" || cacheModeInput === "disk") {
|
|
82
|
+
config.wsaa = {
|
|
83
|
+
cache: {
|
|
84
|
+
mode: cacheModeValue ?? cacheModeInput,
|
|
85
|
+
directory: readEnv(env, variableNames.wsaaCacheDirectory) ?? ""
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
} else if (cacheModeValue === "memory" || cacheModeInput !== void 0) {
|
|
89
|
+
config.wsaa = {
|
|
90
|
+
cache: {
|
|
91
|
+
mode: cacheModeValue ?? cacheModeInput
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
assertArcaClientConfig(config);
|
|
96
|
+
return normalizeArcaClientConfig(config);
|
|
97
|
+
}
|
|
98
|
+
function assertArcaClientConfig(config) {
|
|
99
|
+
const invalidFields = [];
|
|
100
|
+
const normalized = normalizeArcaClientConfig(config);
|
|
101
|
+
if (!/^\d{11}$/.test(normalized.taxId)) {
|
|
102
|
+
invalidFields.push("taxId");
|
|
103
|
+
}
|
|
104
|
+
if (!normalized.certificatePem.startsWith("-----BEGIN CERTIFICATE-----")) {
|
|
105
|
+
invalidFields.push("certificatePem");
|
|
106
|
+
}
|
|
107
|
+
if (!PRIVATE_KEY_PEM_PREFIXES.some(
|
|
108
|
+
(prefix) => normalized.privateKeyPem.startsWith(prefix)
|
|
109
|
+
)) {
|
|
110
|
+
invalidFields.push("privateKeyPem");
|
|
111
|
+
}
|
|
112
|
+
if (!ARCA_ENVIRONMENTS.includes(normalized.environment)) {
|
|
113
|
+
invalidFields.push("environment");
|
|
114
|
+
}
|
|
115
|
+
const cacheConfig = config.wsaa?.cache;
|
|
116
|
+
if (cacheConfig) {
|
|
117
|
+
const cacheMode = cacheConfig.mode?.trim().toLowerCase() ?? "memory";
|
|
118
|
+
if (cacheMode !== "memory" && cacheMode !== "disk") {
|
|
119
|
+
invalidFields.push("wsaa.cache.mode");
|
|
120
|
+
}
|
|
121
|
+
if (cacheMode === "disk" && (!("directory" in cacheConfig) || cacheConfig.directory.trim().length < 1)) {
|
|
122
|
+
invalidFields.push("wsaa.cache.directory");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (invalidFields.length > 0) {
|
|
126
|
+
throw new ArcaConfigurationError(
|
|
127
|
+
`Missing or invalid ARCA client config fields: ${invalidFields.join(", ")}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
var ARCA_WSAA_CONFIG = {
|
|
132
|
+
namespace: "http://wsaa.view.sua.dvadac.desein.afip.gov",
|
|
133
|
+
endpoint: {
|
|
134
|
+
production: "https://wsaa.afip.gov.ar/ws/services/LoginCms",
|
|
135
|
+
test: "https://wsaahomo.afip.gov.ar/ws/services/LoginCms"
|
|
136
|
+
},
|
|
137
|
+
soapVersion: "1.1",
|
|
138
|
+
soapActionBase: "",
|
|
139
|
+
usesEmptySoapAction: true
|
|
140
|
+
};
|
|
141
|
+
var ARCA_SERVICE_CONFIG = {
|
|
142
|
+
wsaa: ARCA_WSAA_CONFIG,
|
|
143
|
+
wsfe: {
|
|
144
|
+
namespace: "http://ar.gov.afip.dif.FEV1/",
|
|
145
|
+
endpoint: {
|
|
146
|
+
production: "https://servicios1.afip.gov.ar/wsfev1/service.asmx",
|
|
147
|
+
test: "https://wswhomo.afip.gov.ar/wsfev1/service.asmx"
|
|
148
|
+
},
|
|
149
|
+
soapVersion: "1.2",
|
|
150
|
+
soapActionBase: "http://ar.gov.afip.dif.FEV1/",
|
|
151
|
+
useLegacyTlsSecurityLevel0: true
|
|
152
|
+
},
|
|
153
|
+
wsmtxca: {
|
|
154
|
+
namespace: "http://impl.service.wsmtxca.afip.gov.ar/service/",
|
|
155
|
+
endpoint: {
|
|
156
|
+
production: "https://serviciosjava.afip.gov.ar/wsmtxca/services/MTXCAService",
|
|
157
|
+
test: "https://fwshomo.afip.gov.ar/wsmtxca/services/MTXCAService"
|
|
158
|
+
},
|
|
159
|
+
soapVersion: "1.1",
|
|
160
|
+
soapActionBase: "http://impl.service.wsmtxca.afip.gov.ar/service/"
|
|
161
|
+
},
|
|
162
|
+
"padron-a5": {
|
|
163
|
+
namespace: "http://a5.soap.ws.server.puc.sr/",
|
|
164
|
+
endpoint: {
|
|
165
|
+
production: "https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA5",
|
|
166
|
+
test: "https://awshomo.afip.gov.ar/sr-padron/webservices/personaServiceA5"
|
|
167
|
+
},
|
|
168
|
+
soapVersion: "1.1",
|
|
169
|
+
soapActionBase: "",
|
|
170
|
+
usesEmptySoapAction: true
|
|
171
|
+
},
|
|
172
|
+
"padron-a13": {
|
|
173
|
+
namespace: "http://a13.soap.ws.server.puc.sr/",
|
|
174
|
+
endpoint: {
|
|
175
|
+
production: "https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA13",
|
|
176
|
+
test: "https://awshomo.afip.gov.ar/sr-padron/webservices/personaServiceA13"
|
|
177
|
+
},
|
|
178
|
+
soapVersion: "1.1",
|
|
179
|
+
soapActionBase: "",
|
|
180
|
+
usesEmptySoapAction: true
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
function getArcaServiceConfig(service) {
|
|
184
|
+
const serviceConfig = ARCA_SERVICE_CONFIG[service];
|
|
185
|
+
if (!serviceConfig) {
|
|
186
|
+
throw new ArcaConfigurationError(
|
|
187
|
+
`Unsupported ARCA service configuration: ${service}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return serviceConfig;
|
|
191
|
+
}
|
|
192
|
+
function normalizeArcaClientConfig(config) {
|
|
193
|
+
const normalizedEnvironment = normalizeEnvironmentValue(String(config.environment)) ?? config.environment;
|
|
194
|
+
const normalizedCache = normalizeWsaaCacheConfig(config.wsaa?.cache);
|
|
195
|
+
return {
|
|
196
|
+
taxId: config.taxId.trim(),
|
|
197
|
+
certificatePem: config.certificatePem.trim(),
|
|
198
|
+
privateKeyPem: config.privateKeyPem.trim(),
|
|
199
|
+
environment: normalizedEnvironment,
|
|
200
|
+
...normalizedCache ? {
|
|
201
|
+
wsaa: {
|
|
202
|
+
cache: normalizedCache
|
|
203
|
+
}
|
|
204
|
+
} : {}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function normalizeWsaaCacheConfig(cache) {
|
|
208
|
+
if (!cache) {
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
const mode = normalizeCacheMode(cache.mode) ?? "memory";
|
|
212
|
+
if (mode !== "disk") {
|
|
213
|
+
return {
|
|
214
|
+
mode: "memory"
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
mode,
|
|
219
|
+
directory: ("directory" in cache ? cache.directory : "").trim()
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function normalizeEnvironmentValue(value) {
|
|
223
|
+
if (!value) {
|
|
224
|
+
return void 0;
|
|
225
|
+
}
|
|
226
|
+
const normalized = value.trim().toLowerCase();
|
|
227
|
+
if (ARCA_ENVIRONMENTS.includes(normalized)) {
|
|
228
|
+
return normalized;
|
|
229
|
+
}
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
function normalizeCacheMode(value) {
|
|
233
|
+
if (!value) {
|
|
234
|
+
return void 0;
|
|
235
|
+
}
|
|
236
|
+
const normalized = value.trim().toLowerCase();
|
|
237
|
+
if (normalized === "memory" || normalized === "disk") {
|
|
238
|
+
return normalized;
|
|
239
|
+
}
|
|
240
|
+
return void 0;
|
|
241
|
+
}
|
|
242
|
+
function readEnv(env, variableName) {
|
|
243
|
+
return env[variableName]?.trim() || void 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/services/padron.ts
|
|
247
|
+
function createPadronService(options) {
|
|
248
|
+
return {
|
|
249
|
+
async getTaxpayerDetails(taxId) {
|
|
250
|
+
const raw = await executePadronOperation(
|
|
251
|
+
options,
|
|
252
|
+
"padron-a5",
|
|
253
|
+
"getPersona_v2",
|
|
254
|
+
{
|
|
255
|
+
idPersona: Number.parseInt(String(taxId), 10)
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
if (!raw) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const record = raw;
|
|
262
|
+
const datosGenerales = record.datosGenerales;
|
|
263
|
+
return {
|
|
264
|
+
taxId: String(record.idPersona ?? ""),
|
|
265
|
+
...record.tipoPersona !== void 0 ? { personType: String(record.tipoPersona) } : {},
|
|
266
|
+
...datosGenerales ? { name: extractPadronName(datosGenerales) } : {},
|
|
267
|
+
raw: record
|
|
268
|
+
};
|
|
269
|
+
},
|
|
270
|
+
async getTaxIdByDocument(documentNumber) {
|
|
271
|
+
const raw = await executePadronOperation(
|
|
272
|
+
options,
|
|
273
|
+
"padron-a13",
|
|
274
|
+
"getIdPersonaListByDocumento",
|
|
275
|
+
{
|
|
276
|
+
documento: String(documentNumber)
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
if (!raw) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
const record = raw;
|
|
283
|
+
const idPersona = record.idPersona;
|
|
284
|
+
const taxIds = Array.isArray(idPersona) ? idPersona.map(String) : idPersona !== void 0 ? [String(idPersona)] : [];
|
|
285
|
+
return {
|
|
286
|
+
taxIds,
|
|
287
|
+
raw: record
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function extractPadronName(datosGenerales) {
|
|
293
|
+
if (typeof datosGenerales.razonSocial === "string") {
|
|
294
|
+
return datosGenerales.razonSocial;
|
|
295
|
+
}
|
|
296
|
+
const nombre = datosGenerales.nombre;
|
|
297
|
+
const apellido = datosGenerales.apellido;
|
|
298
|
+
if (typeof apellido === "string" && typeof nombre === "string") {
|
|
299
|
+
return `${apellido} ${nombre}`.trim();
|
|
300
|
+
}
|
|
301
|
+
if (typeof apellido === "string") {
|
|
302
|
+
return apellido;
|
|
303
|
+
}
|
|
304
|
+
if (typeof nombre === "string") {
|
|
305
|
+
return nombre;
|
|
306
|
+
}
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
async function executePadronOperation(options, service, operation, body) {
|
|
310
|
+
const auth = await options.auth.login(
|
|
311
|
+
service === "padron-a5" ? "ws_sr_constancia_inscripcion" : "ws_sr_padron_a13"
|
|
312
|
+
);
|
|
313
|
+
try {
|
|
314
|
+
const response = await options.soap.execute({
|
|
315
|
+
service,
|
|
316
|
+
operation,
|
|
317
|
+
bodyElementNamespaceMode: "prefix",
|
|
318
|
+
body: {
|
|
319
|
+
token: auth.token,
|
|
320
|
+
sign: auth.sign,
|
|
321
|
+
cuitRepresentada: Number.parseInt(options.config.taxId, 10),
|
|
322
|
+
...body
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
const operationResponse = response.result;
|
|
326
|
+
if (operation === "getPersona_v2") {
|
|
327
|
+
return operationResponse.personaReturn ?? null;
|
|
328
|
+
}
|
|
329
|
+
if (operation === "getIdPersonaListByDocumento") {
|
|
330
|
+
return operationResponse.idPersonaListReturn ?? null;
|
|
331
|
+
}
|
|
332
|
+
return operationResponse.return ?? null;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (error instanceof ArcaSoapFaultError && error.message.toLowerCase().includes("no existe")) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/services/wsfe.ts
|
|
342
|
+
function createWsfeService(options) {
|
|
343
|
+
async function getLastVoucher({
|
|
344
|
+
representedTaxId,
|
|
345
|
+
salesPoint,
|
|
346
|
+
voucherType,
|
|
347
|
+
forceAuthRefresh
|
|
348
|
+
}) {
|
|
349
|
+
const auth = await options.auth.login("wsfe", {
|
|
350
|
+
representedTaxId,
|
|
351
|
+
forceRefresh: forceAuthRefresh
|
|
352
|
+
});
|
|
353
|
+
const response = await options.soap.execute({
|
|
354
|
+
service: "wsfe",
|
|
355
|
+
operation: "FECompUltimoAutorizado",
|
|
356
|
+
body: {
|
|
357
|
+
Auth: createWsfeAuth(
|
|
358
|
+
representedTaxId ?? options.config.taxId,
|
|
359
|
+
auth.token,
|
|
360
|
+
auth.sign
|
|
361
|
+
),
|
|
362
|
+
PtoVta: salesPoint,
|
|
363
|
+
CbteTipo: voucherType
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
const result = unwrapWsfeOperationResult(
|
|
367
|
+
"FECompUltimoAutorizado",
|
|
368
|
+
response.result
|
|
369
|
+
);
|
|
370
|
+
return Number(result.CbteNro ?? 0) + 1;
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
async createNextVoucher({ representedTaxId, data }) {
|
|
374
|
+
const voucherNumber = await getLastVoucher({
|
|
375
|
+
representedTaxId,
|
|
376
|
+
salesPoint: data.salesPoint,
|
|
377
|
+
voucherType: data.voucherType
|
|
378
|
+
});
|
|
379
|
+
const requestData = mapWsfeVoucherInput(data, voucherNumber);
|
|
380
|
+
const auth = await options.auth.login("wsfe", { representedTaxId });
|
|
381
|
+
const response = await options.soap.execute({
|
|
382
|
+
service: "wsfe",
|
|
383
|
+
operation: "FECAESolicitar",
|
|
384
|
+
body: {
|
|
385
|
+
Auth: createWsfeAuth(
|
|
386
|
+
representedTaxId ?? options.config.taxId,
|
|
387
|
+
auth.token,
|
|
388
|
+
auth.sign
|
|
389
|
+
),
|
|
390
|
+
FeCAEReq: {
|
|
391
|
+
FeCabReq: {
|
|
392
|
+
CantReg: 1,
|
|
393
|
+
PtoVta: data.salesPoint,
|
|
394
|
+
CbteTipo: data.voucherType
|
|
395
|
+
},
|
|
396
|
+
FeDetReq: {
|
|
397
|
+
FECAEDetRequest: requestData
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
const result = unwrapWsfeOperationResult(
|
|
403
|
+
"FECAESolicitar",
|
|
404
|
+
response.result
|
|
405
|
+
);
|
|
406
|
+
const detailResponse = normalizeWsfeDetailResponse(result);
|
|
407
|
+
const cae = detailResponse.CAE;
|
|
408
|
+
const caeExpiry = detailResponse.CAEFchVto;
|
|
409
|
+
if (typeof cae !== "string" || typeof caeExpiry !== "string") {
|
|
410
|
+
throw new ArcaServiceError(
|
|
411
|
+
"WSFE did not return CAE authorization data",
|
|
412
|
+
{ detail: result }
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
cae,
|
|
417
|
+
caeExpiry: String(caeExpiry),
|
|
418
|
+
voucherNumber,
|
|
419
|
+
raw: result
|
|
420
|
+
};
|
|
421
|
+
},
|
|
422
|
+
getLastVoucher,
|
|
423
|
+
async getSalesPoints({ representedTaxId, forceAuthRefresh }) {
|
|
424
|
+
const auth = await options.auth.login("wsfe", {
|
|
425
|
+
representedTaxId,
|
|
426
|
+
forceRefresh: forceAuthRefresh
|
|
427
|
+
});
|
|
428
|
+
const response = await options.soap.execute({
|
|
429
|
+
service: "wsfe",
|
|
430
|
+
operation: "FEParamGetPtosVenta",
|
|
431
|
+
body: {
|
|
432
|
+
Auth: createWsfeAuth(
|
|
433
|
+
representedTaxId ?? options.config.taxId,
|
|
434
|
+
auth.token,
|
|
435
|
+
auth.sign
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
const result = unwrapWsfeOperationResult(
|
|
440
|
+
"FEParamGetPtosVenta",
|
|
441
|
+
response.result
|
|
442
|
+
);
|
|
443
|
+
const resultGet = result.ResultGet;
|
|
444
|
+
const rawPoints = resultGet?.PtoVenta;
|
|
445
|
+
if (!rawPoints) {
|
|
446
|
+
return [];
|
|
447
|
+
}
|
|
448
|
+
const entries = Array.isArray(rawPoints) ? rawPoints : [rawPoints];
|
|
449
|
+
return entries.map(mapWsfeSalesPoint);
|
|
450
|
+
},
|
|
451
|
+
async getVoucherInfo({
|
|
452
|
+
representedTaxId,
|
|
453
|
+
number,
|
|
454
|
+
salesPoint,
|
|
455
|
+
voucherType
|
|
456
|
+
}) {
|
|
457
|
+
const auth = await options.auth.login("wsfe", { representedTaxId });
|
|
458
|
+
const response = await options.soap.execute({
|
|
459
|
+
service: "wsfe",
|
|
460
|
+
operation: "FECompConsultar",
|
|
461
|
+
body: {
|
|
462
|
+
Auth: createWsfeAuth(
|
|
463
|
+
representedTaxId ?? options.config.taxId,
|
|
464
|
+
auth.token,
|
|
465
|
+
auth.sign
|
|
466
|
+
),
|
|
467
|
+
FeCompConsReq: {
|
|
468
|
+
CbteNro: number,
|
|
469
|
+
PtoVta: salesPoint,
|
|
470
|
+
CbteTipo: voucherType
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
const result = unwrapWsfeOperationResult(
|
|
475
|
+
"FECompConsultar",
|
|
476
|
+
response.result
|
|
477
|
+
);
|
|
478
|
+
const raw = result.ResultGet ?? null;
|
|
479
|
+
if (!raw) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
return mapWsfeVoucherInfo(raw);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
function mapWsfeVoucherInput(input, voucherNumber) {
|
|
487
|
+
const data = {
|
|
488
|
+
Concepto: input.concept,
|
|
489
|
+
DocTipo: input.documentType,
|
|
490
|
+
DocNro: input.documentNumber,
|
|
491
|
+
CbteDesde: voucherNumber,
|
|
492
|
+
CbteHasta: voucherNumber,
|
|
493
|
+
CbteFch: input.voucherDate,
|
|
494
|
+
ImpTotal: input.totalAmount,
|
|
495
|
+
ImpTotConc: input.nonTaxableAmount,
|
|
496
|
+
ImpNeto: input.netAmount,
|
|
497
|
+
ImpOpEx: input.exemptAmount,
|
|
498
|
+
ImpTrib: input.taxAmount,
|
|
499
|
+
ImpIVA: input.vatAmount,
|
|
500
|
+
MonId: input.currencyId,
|
|
501
|
+
MonCotiz: input.exchangeRate,
|
|
502
|
+
PtoVta: input.salesPoint,
|
|
503
|
+
CbteTipo: input.voucherType
|
|
504
|
+
};
|
|
505
|
+
if (input.receiverVatConditionId !== void 0) {
|
|
506
|
+
data.CondicionIVAReceptorId = input.receiverVatConditionId;
|
|
507
|
+
}
|
|
508
|
+
if (input.serviceStartDate !== void 0) {
|
|
509
|
+
data.FchServDesde = input.serviceStartDate;
|
|
510
|
+
}
|
|
511
|
+
if (input.serviceEndDate !== void 0) {
|
|
512
|
+
data.FchServHasta = input.serviceEndDate;
|
|
513
|
+
}
|
|
514
|
+
if (input.paymentDueDate !== void 0) {
|
|
515
|
+
data.FchVtoPago = input.paymentDueDate;
|
|
516
|
+
}
|
|
517
|
+
if (input.associatedVouchers) {
|
|
518
|
+
data.CbtesAsoc = {
|
|
519
|
+
CbteAsoc: input.associatedVouchers.map((v) => ({
|
|
520
|
+
Tipo: v.type,
|
|
521
|
+
PtoVta: v.salesPoint,
|
|
522
|
+
Nro: v.number,
|
|
523
|
+
...v.taxId !== void 0 ? { Cuit: v.taxId } : {},
|
|
524
|
+
...v.voucherDate !== void 0 ? { CbteFch: v.voucherDate } : {}
|
|
525
|
+
}))
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
if (input.taxes) {
|
|
529
|
+
data.Tributos = {
|
|
530
|
+
Tributo: input.taxes.map((t) => ({
|
|
531
|
+
Id: t.id,
|
|
532
|
+
...t.description !== void 0 ? { Desc: t.description } : {},
|
|
533
|
+
BaseImp: t.baseAmount,
|
|
534
|
+
Alic: t.rate,
|
|
535
|
+
Importe: t.amount
|
|
536
|
+
}))
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (input.vatRates) {
|
|
540
|
+
data.Iva = {
|
|
541
|
+
AlicIva: input.vatRates.map((v) => ({
|
|
542
|
+
Id: v.id,
|
|
543
|
+
BaseImp: v.baseAmount,
|
|
544
|
+
Importe: v.amount
|
|
545
|
+
}))
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
if (input.optionalFields) {
|
|
549
|
+
data.Opcionales = {
|
|
550
|
+
Opcional: input.optionalFields.map((o) => ({
|
|
551
|
+
Id: o.id,
|
|
552
|
+
Valor: o.value
|
|
553
|
+
}))
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
if (input.buyers) {
|
|
557
|
+
data.Compradores = {
|
|
558
|
+
Comprador: input.buyers.map((b) => ({
|
|
559
|
+
DocTipo: b.documentType,
|
|
560
|
+
DocNro: b.documentNumber,
|
|
561
|
+
Porcentaje: b.percentage
|
|
562
|
+
}))
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
return data;
|
|
566
|
+
}
|
|
567
|
+
function mapWsfeSalesPoint(raw) {
|
|
568
|
+
const record = raw;
|
|
569
|
+
return {
|
|
570
|
+
number: Number(record.Nro ?? 0),
|
|
571
|
+
...record.EmisionTipo !== void 0 ? { emissionType: String(record.EmisionTipo) } : {},
|
|
572
|
+
...record.Bloqueado !== void 0 ? { blocked: String(record.Bloqueado) } : {},
|
|
573
|
+
...record.FchBaja !== void 0 ? { deletedSince: String(record.FchBaja) } : {}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function mapWsfeVoucherInfo(raw) {
|
|
577
|
+
return {
|
|
578
|
+
voucherNumber: Number(raw.CbteDesde ?? raw.CbteHasta ?? 0),
|
|
579
|
+
...raw.CbteFch !== void 0 ? { voucherDate: String(raw.CbteFch) } : {},
|
|
580
|
+
...raw.PtoVta !== void 0 ? { salesPoint: Number(raw.PtoVta) } : {},
|
|
581
|
+
...raw.CbteTipo !== void 0 ? { voucherType: Number(raw.CbteTipo) } : {},
|
|
582
|
+
...raw.ImpTotal !== void 0 ? { totalAmount: Number(raw.ImpTotal) } : {},
|
|
583
|
+
...raw.Resultado !== void 0 ? { result: String(raw.Resultado) } : {},
|
|
584
|
+
...raw.CAE !== void 0 ? { cae: String(raw.CAE) } : {},
|
|
585
|
+
...raw.CAEFchVto !== void 0 ? { caeExpiry: String(raw.CAEFchVto) } : {},
|
|
586
|
+
raw
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
function createWsfeAuth(representedTaxId, token, sign) {
|
|
590
|
+
return {
|
|
591
|
+
Token: token,
|
|
592
|
+
Sign: sign,
|
|
593
|
+
Cuit: Number.parseInt(String(representedTaxId), 10)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function unwrapWsfeOperationResult(operation, response) {
|
|
597
|
+
const operationResponse = response[`${operation}Response`];
|
|
598
|
+
const result = operationResponse?.[`${operation}Result`] ?? response[`${operation}Result`] ?? response;
|
|
599
|
+
if (operation === "FECAESolicitar") {
|
|
600
|
+
const detailResponse = normalizeWsfeDetailResponse(result);
|
|
601
|
+
const resultCode = detailResponse.Resultado;
|
|
602
|
+
if (resultCode && resultCode !== "A") {
|
|
603
|
+
const observationsContainer = detailResponse.Observaciones;
|
|
604
|
+
const observations = normalizeWsfeErrors(observationsContainer?.Obs);
|
|
605
|
+
if (observations.length > 0) {
|
|
606
|
+
const firstObservation = observations[0];
|
|
607
|
+
if (!firstObservation) {
|
|
608
|
+
throw new ArcaServiceError(
|
|
609
|
+
"WSFE returned an empty observation list",
|
|
610
|
+
{
|
|
611
|
+
detail: result
|
|
612
|
+
}
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
throw new ArcaServiceError(firstObservation.message, {
|
|
616
|
+
serviceCode: firstObservation.code,
|
|
617
|
+
detail: result
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const errorsContainer = result.Errors;
|
|
623
|
+
const errors = normalizeWsfeErrors(errorsContainer?.Err);
|
|
624
|
+
if (errors.length > 0) {
|
|
625
|
+
const firstError = errors[0];
|
|
626
|
+
if (!firstError) {
|
|
627
|
+
throw new ArcaServiceError("WSFE returned an empty error list", {
|
|
628
|
+
detail: result
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
throw new ArcaServiceError(firstError.message, {
|
|
632
|
+
serviceCode: firstError.code,
|
|
633
|
+
detail: result
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
return result;
|
|
637
|
+
}
|
|
638
|
+
function normalizeWsfeDetailResponse(result) {
|
|
639
|
+
const detailResponse = result.FeDetResp;
|
|
640
|
+
const rawDetail = detailResponse?.FECAEDetResponse;
|
|
641
|
+
if (Array.isArray(rawDetail)) {
|
|
642
|
+
return rawDetail[0] ?? {};
|
|
643
|
+
}
|
|
644
|
+
return rawDetail ?? {};
|
|
645
|
+
}
|
|
646
|
+
function normalizeWsfeErrors(rawErrors) {
|
|
647
|
+
const entries = Array.isArray(rawErrors) ? rawErrors : rawErrors ? [rawErrors] : [];
|
|
648
|
+
return entries.map((entry) => entry).map((entry) => {
|
|
649
|
+
const code = entry.Code ?? entry.code ?? "N/A";
|
|
650
|
+
const message = entry.Msg ?? entry.msg ?? "Unknown WSFE error";
|
|
651
|
+
return {
|
|
652
|
+
code: String(code),
|
|
653
|
+
message: `(${String(code)}) ${String(message)}`
|
|
654
|
+
};
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/services/wsmtxca.ts
|
|
659
|
+
function createWsmtxcaService(options) {
|
|
660
|
+
return {
|
|
661
|
+
async authorizeVoucher({ representedTaxId, data }) {
|
|
662
|
+
const auth = await options.auth.login("wsmtxca", { representedTaxId });
|
|
663
|
+
const response = await options.soap.execute({
|
|
664
|
+
service: "wsmtxca",
|
|
665
|
+
operation: "autorizarComprobante",
|
|
666
|
+
bodyElementName: "autorizarComprobanteRequest",
|
|
667
|
+
bodyElementNamespaceMode: "prefix",
|
|
668
|
+
body: {
|
|
669
|
+
authRequest: createWsmtxcaAuth(
|
|
670
|
+
representedTaxId ?? options.config.taxId,
|
|
671
|
+
auth.token,
|
|
672
|
+
auth.sign
|
|
673
|
+
),
|
|
674
|
+
...data
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
const raw = unwrapWsmtxcaOperationResponse(
|
|
678
|
+
response.result,
|
|
679
|
+
"autorizarComprobante"
|
|
680
|
+
);
|
|
681
|
+
const authorizationPayload = extractWsmtxcaAuthorizationPayload(raw);
|
|
682
|
+
const messages = extractWsmtxcaMessages(raw);
|
|
683
|
+
const resultado = raw.resultado ?? authorizationPayload.resultado;
|
|
684
|
+
const caeValue = authorizationPayload.CAE ?? authorizationPayload.codigoAutorizacion ?? raw.codigoAutorizacion;
|
|
685
|
+
if (resultado === "R" || caeValue == null) {
|
|
686
|
+
throw new ArcaServiceError(
|
|
687
|
+
messages.join(" | ") || "WSMTXCA rejected the voucher authorization",
|
|
688
|
+
{ detail: raw }
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
cae: String(caeValue),
|
|
693
|
+
caeExpiry: normalizeWsmtxcaResponseDate(
|
|
694
|
+
authorizationPayload.fechaVencimientoCAE ?? authorizationPayload.fechaVencimiento ?? raw.fechaVencimiento
|
|
695
|
+
),
|
|
696
|
+
voucherNumber: parseWsmtxcaVoucherNumber(
|
|
697
|
+
authorizationPayload.numeroComprobante ?? raw.numeroComprobante,
|
|
698
|
+
"WSMTXCA did not return the authorized voucher number",
|
|
699
|
+
raw
|
|
700
|
+
),
|
|
701
|
+
messages,
|
|
702
|
+
raw
|
|
703
|
+
};
|
|
704
|
+
},
|
|
705
|
+
async getLastAuthorizedVoucher({
|
|
706
|
+
representedTaxId,
|
|
707
|
+
voucherType,
|
|
708
|
+
salesPoint
|
|
709
|
+
}) {
|
|
710
|
+
const auth = await options.auth.login("wsmtxca", { representedTaxId });
|
|
711
|
+
const response = await options.soap.execute({
|
|
712
|
+
service: "wsmtxca",
|
|
713
|
+
operation: "consultarUltimoComprobanteAutorizado",
|
|
714
|
+
bodyElementName: "consultarUltimoComprobanteAutorizadoRequest",
|
|
715
|
+
bodyElementNamespaceMode: "prefix",
|
|
716
|
+
body: {
|
|
717
|
+
authRequest: createWsmtxcaAuth(
|
|
718
|
+
representedTaxId ?? options.config.taxId,
|
|
719
|
+
auth.token,
|
|
720
|
+
auth.sign
|
|
721
|
+
),
|
|
722
|
+
consultaUltimoComprobanteAutorizadoRequest: {
|
|
723
|
+
codigoTipoComprobante: voucherType,
|
|
724
|
+
numeroPuntoVenta: salesPoint
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
const raw = unwrapWsmtxcaOperationResponse(
|
|
729
|
+
response.result,
|
|
730
|
+
"consultarUltimoComprobanteAutorizado"
|
|
731
|
+
);
|
|
732
|
+
return {
|
|
733
|
+
voucherNumber: parseWsmtxcaVoucherNumber(
|
|
734
|
+
raw.numeroComprobante ?? raw.cbteNro ?? raw.nroComprobante,
|
|
735
|
+
extractWsmtxcaMessages(raw).join(" | ") || "WSMTXCA did not return the last authorized voucher number",
|
|
736
|
+
raw
|
|
737
|
+
),
|
|
738
|
+
raw
|
|
739
|
+
};
|
|
740
|
+
},
|
|
741
|
+
async getVoucher({
|
|
742
|
+
representedTaxId,
|
|
743
|
+
voucherType,
|
|
744
|
+
salesPoint,
|
|
745
|
+
voucherNumber
|
|
746
|
+
}) {
|
|
747
|
+
const auth = await options.auth.login("wsmtxca", { representedTaxId });
|
|
748
|
+
const response = await options.soap.execute({
|
|
749
|
+
service: "wsmtxca",
|
|
750
|
+
operation: "consultarComprobante",
|
|
751
|
+
bodyElementName: "consultarComprobanteRequest",
|
|
752
|
+
bodyElementNamespaceMode: "prefix",
|
|
753
|
+
body: {
|
|
754
|
+
authRequest: createWsmtxcaAuth(
|
|
755
|
+
representedTaxId ?? options.config.taxId,
|
|
756
|
+
auth.token,
|
|
757
|
+
auth.sign
|
|
758
|
+
),
|
|
759
|
+
consultaComprobanteRequest: {
|
|
760
|
+
codigoTipoComprobante: voucherType,
|
|
761
|
+
numeroPuntoVenta: salesPoint,
|
|
762
|
+
numeroComprobante: voucherNumber
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
const raw = unwrapWsmtxcaOperationResponse(
|
|
767
|
+
response.result,
|
|
768
|
+
"consultarComprobante"
|
|
769
|
+
);
|
|
770
|
+
const voucher = extractWsmtxcaVoucherPayload(raw);
|
|
771
|
+
const messages = extractWsmtxcaMessages(raw);
|
|
772
|
+
const invoiceDate = normalizeWsmtxcaResponseDate(
|
|
773
|
+
voucher.fechaEmision ?? voucher.fecha ?? voucher.CbteFch
|
|
774
|
+
);
|
|
775
|
+
if (!invoiceDate) {
|
|
776
|
+
throw new ArcaServiceError(
|
|
777
|
+
messages[0] ?? "WSMTXCA did not return the voucher issue date",
|
|
778
|
+
{ detail: raw }
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
return {
|
|
782
|
+
invoiceDate,
|
|
783
|
+
voucher,
|
|
784
|
+
messages,
|
|
785
|
+
raw
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
function createWsmtxcaAuth(representedTaxId, token, sign) {
|
|
791
|
+
return {
|
|
792
|
+
token,
|
|
793
|
+
sign,
|
|
794
|
+
cuitRepresentada: Number.parseInt(String(representedTaxId), 10)
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function toRecord(value) {
|
|
798
|
+
return value && typeof value === "object" ? value : void 0;
|
|
799
|
+
}
|
|
800
|
+
function unwrapWsmtxcaOperationResponse(response, operation) {
|
|
801
|
+
const responseRecord = toRecord(response) ?? {};
|
|
802
|
+
if (operation === "autorizarComprobante") {
|
|
803
|
+
return toRecord(responseRecord.autorizarComprobanteResponse) ?? toRecord(responseRecord.autorizarComprobanteResult) ?? toRecord(responseRecord.comprobanteCAEResponse) ?? toRecord(responseRecord.comprobanteCAEReponse) ?? responseRecord;
|
|
804
|
+
}
|
|
805
|
+
if (operation === "consultarComprobante") {
|
|
806
|
+
return toRecord(responseRecord.consultarComprobanteResponse) ?? toRecord(responseRecord.consultaComprobanteResponse) ?? toRecord(responseRecord.consultarComprobanteResult) ?? responseRecord;
|
|
807
|
+
}
|
|
808
|
+
return toRecord(responseRecord.consultarUltimoComprobanteAutorizadoResponse) ?? toRecord(responseRecord.consultaUltimoComprobanteAutorizadoResponse) ?? toRecord(responseRecord.consultarUltimoComprobanteAutorizadoResult) ?? responseRecord;
|
|
809
|
+
}
|
|
810
|
+
function extractWsmtxcaAuthorizationPayload(raw) {
|
|
811
|
+
return toRecord(raw.comprobanteResponse) ?? toRecord(raw.comprobanteCAEResponse) ?? toRecord(raw.comprobanteCAEReponse) ?? raw;
|
|
812
|
+
}
|
|
813
|
+
function extractWsmtxcaVoucherPayload(raw) {
|
|
814
|
+
return toRecord(raw.comprobanteResponse) ?? toRecord(raw.comprobante) ?? toRecord(raw.cmp) ?? raw;
|
|
815
|
+
}
|
|
816
|
+
function extractWsmtxcaMessages(raw) {
|
|
817
|
+
const rawErrors = raw.arrayErrores;
|
|
818
|
+
const rawObservations = raw.arrayObservaciones;
|
|
819
|
+
const toEntries = (value) => {
|
|
820
|
+
if (!value) {
|
|
821
|
+
return [];
|
|
822
|
+
}
|
|
823
|
+
if (Array.isArray(value)) {
|
|
824
|
+
return value;
|
|
825
|
+
}
|
|
826
|
+
if (typeof value === "object") {
|
|
827
|
+
return [value];
|
|
828
|
+
}
|
|
829
|
+
return [];
|
|
830
|
+
};
|
|
831
|
+
const errors = toEntries(rawErrors?.codigoDescripcion).map((entry) => {
|
|
832
|
+
const code = entry.codigo == null ? "N/A" : String(entry.codigo);
|
|
833
|
+
const description = entry.descripcion == null ? "Unknown WSMTXCA error" : String(entry.descripcion);
|
|
834
|
+
return `Error ${code}: ${description}`;
|
|
835
|
+
});
|
|
836
|
+
const observations = toEntries(rawObservations?.codigoDescripcion).map(
|
|
837
|
+
(entry) => {
|
|
838
|
+
const code = entry.codigo == null ? "N/A" : String(entry.codigo);
|
|
839
|
+
const description = entry.descripcion == null ? "" : String(entry.descripcion);
|
|
840
|
+
return `Obs ${code}: ${description}`.trim();
|
|
841
|
+
}
|
|
842
|
+
);
|
|
843
|
+
return [...errors, ...observations];
|
|
844
|
+
}
|
|
845
|
+
function parseWsmtxcaVoucherNumber(value, message, detail) {
|
|
846
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
847
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
848
|
+
throw new ArcaServiceError(message, { detail });
|
|
849
|
+
}
|
|
850
|
+
return parsed;
|
|
851
|
+
}
|
|
852
|
+
function normalizeWsmtxcaResponseDate(value) {
|
|
853
|
+
if (typeof value === "number" && Number.isInteger(value)) {
|
|
854
|
+
return formatCompactDateToIso(value);
|
|
855
|
+
}
|
|
856
|
+
if (typeof value !== "string") {
|
|
857
|
+
return void 0;
|
|
858
|
+
}
|
|
859
|
+
const trimmed = value.trim();
|
|
860
|
+
if (!trimmed) {
|
|
861
|
+
return void 0;
|
|
862
|
+
}
|
|
863
|
+
if (/^\d{8}$/.test(trimmed)) {
|
|
864
|
+
return formatCompactDateToIso(Number.parseInt(trimmed, 10));
|
|
865
|
+
}
|
|
866
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) {
|
|
867
|
+
return trimmed.slice(0, 10);
|
|
868
|
+
}
|
|
869
|
+
return void 0;
|
|
870
|
+
}
|
|
871
|
+
function formatCompactDateToIso(dateValue) {
|
|
872
|
+
if (!dateValue) {
|
|
873
|
+
return void 0;
|
|
874
|
+
}
|
|
875
|
+
const raw = String(dateValue);
|
|
876
|
+
if (raw.length !== 8) {
|
|
877
|
+
return void 0;
|
|
878
|
+
}
|
|
879
|
+
return `${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// src/internal/http.ts
|
|
883
|
+
import https from "https";
|
|
884
|
+
var defaultAgent = new https.Agent({
|
|
885
|
+
keepAlive: true
|
|
886
|
+
});
|
|
887
|
+
var legacyTlsAgent = new https.Agent({
|
|
888
|
+
keepAlive: true,
|
|
889
|
+
ciphers: "DEFAULT@SECLEVEL=0"
|
|
890
|
+
});
|
|
891
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
892
|
+
async function postXml({
|
|
893
|
+
url,
|
|
894
|
+
body,
|
|
895
|
+
contentType,
|
|
896
|
+
soapAction,
|
|
897
|
+
useLegacyTlsSecurityLevel0 = false
|
|
898
|
+
}) {
|
|
899
|
+
const endpoint = new URL(url);
|
|
900
|
+
const requestBody = Buffer.from(body, "utf8");
|
|
901
|
+
return await new Promise((resolve, reject) => {
|
|
902
|
+
let settled = false;
|
|
903
|
+
const settleResolve = (responseBody) => {
|
|
904
|
+
if (settled) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
settled = true;
|
|
908
|
+
resolve(responseBody);
|
|
909
|
+
};
|
|
910
|
+
const settleReject = (error) => {
|
|
911
|
+
if (settled) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
settled = true;
|
|
915
|
+
reject(error);
|
|
916
|
+
};
|
|
917
|
+
const request = https.request(
|
|
918
|
+
{
|
|
919
|
+
protocol: endpoint.protocol,
|
|
920
|
+
hostname: endpoint.hostname,
|
|
921
|
+
port: endpoint.port || void 0,
|
|
922
|
+
path: `${endpoint.pathname}${endpoint.search}`,
|
|
923
|
+
method: "POST",
|
|
924
|
+
agent: useLegacyTlsSecurityLevel0 ? legacyTlsAgent : defaultAgent,
|
|
925
|
+
headers: {
|
|
926
|
+
Accept: "text/xml, application/soap+xml",
|
|
927
|
+
"Content-Length": requestBody.byteLength,
|
|
928
|
+
"Content-Type": contentType,
|
|
929
|
+
...soapAction === void 0 ? {} : { SOAPAction: `"${soapAction}"` }
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
(response) => {
|
|
933
|
+
const chunks = [];
|
|
934
|
+
const getResponseBody = () => Buffer.concat(chunks).toString("utf8");
|
|
935
|
+
response.on("data", (chunk) => {
|
|
936
|
+
chunks.push(
|
|
937
|
+
typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk
|
|
938
|
+
);
|
|
939
|
+
});
|
|
940
|
+
response.on("error", (error) => {
|
|
941
|
+
settleReject(
|
|
942
|
+
new ArcaTransportError(
|
|
943
|
+
`ARCA HTTP response stream failed: ${error.message}`,
|
|
944
|
+
{
|
|
945
|
+
cause: error,
|
|
946
|
+
statusCode: response.statusCode,
|
|
947
|
+
responseBody: getResponseBody()
|
|
948
|
+
}
|
|
949
|
+
)
|
|
950
|
+
);
|
|
951
|
+
});
|
|
952
|
+
response.on("aborted", () => {
|
|
953
|
+
settleReject(
|
|
954
|
+
new ArcaTransportError("ARCA HTTP response was aborted", {
|
|
955
|
+
statusCode: response.statusCode,
|
|
956
|
+
responseBody: getResponseBody()
|
|
957
|
+
})
|
|
958
|
+
);
|
|
959
|
+
});
|
|
960
|
+
response.on("end", () => {
|
|
961
|
+
const responseBody = getResponseBody();
|
|
962
|
+
const statusCode = response.statusCode ?? 500;
|
|
963
|
+
const responseContentType = Array.isArray(
|
|
964
|
+
response.headers["content-type"]
|
|
965
|
+
) ? response.headers["content-type"].join("; ") : response.headers["content-type"];
|
|
966
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
967
|
+
settleResolve(responseBody);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
if (isXmlLikeResponse(responseBody, responseContentType)) {
|
|
971
|
+
settleResolve(responseBody);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
settleReject(
|
|
975
|
+
new ArcaTransportError(
|
|
976
|
+
`ARCA HTTP request failed with status ${statusCode}`,
|
|
977
|
+
{
|
|
978
|
+
statusCode,
|
|
979
|
+
responseBody
|
|
980
|
+
}
|
|
981
|
+
)
|
|
982
|
+
);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
);
|
|
986
|
+
request.setTimeout(REQUEST_TIMEOUT_MS, () => {
|
|
987
|
+
request.destroy(
|
|
988
|
+
new Error(`ARCA HTTP request timed out after ${REQUEST_TIMEOUT_MS}ms`)
|
|
989
|
+
);
|
|
990
|
+
});
|
|
991
|
+
request.on("error", (error) => {
|
|
992
|
+
settleReject(
|
|
993
|
+
new ArcaTransportError(`ARCA HTTP request failed: ${error.message}`, {
|
|
994
|
+
cause: error
|
|
995
|
+
})
|
|
996
|
+
);
|
|
997
|
+
});
|
|
998
|
+
request.write(requestBody);
|
|
999
|
+
request.end();
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
function isXmlLikeResponse(body, contentType) {
|
|
1003
|
+
const normalizedContentType = contentType?.toLowerCase() ?? "";
|
|
1004
|
+
if (normalizedContentType.includes("xml") || normalizedContentType.includes("soap")) {
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
return body.trimStart().startsWith("<");
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/internal/xml.ts
|
|
1011
|
+
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
1012
|
+
var xmlBuilder = new XMLBuilder({
|
|
1013
|
+
attributeNamePrefix: "@_",
|
|
1014
|
+
format: false,
|
|
1015
|
+
ignoreAttributes: false,
|
|
1016
|
+
suppressBooleanAttributes: false,
|
|
1017
|
+
suppressEmptyNode: true
|
|
1018
|
+
});
|
|
1019
|
+
var xmlParser = new XMLParser({
|
|
1020
|
+
attributeNamePrefix: "@_",
|
|
1021
|
+
ignoreAttributes: false,
|
|
1022
|
+
parseAttributeValue: false,
|
|
1023
|
+
parseTagValue: false,
|
|
1024
|
+
removeNSPrefix: true,
|
|
1025
|
+
trimValues: true
|
|
1026
|
+
});
|
|
1027
|
+
function buildSoapEnvelope(soapVersion, operation, namespace, body, options) {
|
|
1028
|
+
const prefix = soapVersion === "1.2" ? "soap12" : "soap";
|
|
1029
|
+
const envelopeNamespace = soapVersion === "1.2" ? "http://www.w3.org/2003/05/soap-envelope" : "http://schemas.xmlsoap.org/soap/envelope/";
|
|
1030
|
+
const namespaceMode = options?.namespaceMode ?? "default";
|
|
1031
|
+
const operationElementName = namespaceMode === "prefix" ? `tns:${operation}` : operation;
|
|
1032
|
+
const operationNamespaceAttributes = namespaceMode === "prefix" ? { "@_xmlns:tns": namespace } : { "@_xmlns": namespace };
|
|
1033
|
+
const payload = {
|
|
1034
|
+
[`${prefix}:Envelope`]: {
|
|
1035
|
+
"@_xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
|
1036
|
+
"@_xmlns:xsd": "http://www.w3.org/2001/XMLSchema",
|
|
1037
|
+
[`@_xmlns:${prefix}`]: envelopeNamespace,
|
|
1038
|
+
[`${prefix}:Body`]: {
|
|
1039
|
+
[operationElementName]: {
|
|
1040
|
+
...operationNamespaceAttributes,
|
|
1041
|
+
...pruneUndefinedDeep(body)
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
return `<?xml version="1.0" encoding="utf-8"?>${xmlBuilder.build(payload)}`;
|
|
1047
|
+
}
|
|
1048
|
+
function parseSoapBody(xml) {
|
|
1049
|
+
const parsed = xmlParser.parse(xml);
|
|
1050
|
+
const envelope = parsed.Envelope;
|
|
1051
|
+
const body = envelope?.Body;
|
|
1052
|
+
if (!body) {
|
|
1053
|
+
throw new ArcaSoapFaultError("Invalid SOAP response: missing body", {
|
|
1054
|
+
detail: parsed
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
const fault = body.Fault;
|
|
1058
|
+
if (fault) {
|
|
1059
|
+
throw createSoapFaultError(fault);
|
|
1060
|
+
}
|
|
1061
|
+
return body;
|
|
1062
|
+
}
|
|
1063
|
+
function getSingleBodyEntry(body) {
|
|
1064
|
+
const entries = Object.entries(body).filter(([key]) => key !== "@_xmlns");
|
|
1065
|
+
if (entries.length !== 1) {
|
|
1066
|
+
throw new ArcaSoapFaultError(
|
|
1067
|
+
`Invalid SOAP response: expected a single body entry, got ${entries.length}`,
|
|
1068
|
+
{
|
|
1069
|
+
detail: body
|
|
1070
|
+
}
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
return entries[0];
|
|
1074
|
+
}
|
|
1075
|
+
function parseXmlDocument(xml) {
|
|
1076
|
+
return xmlParser.parse(xml);
|
|
1077
|
+
}
|
|
1078
|
+
function pruneUndefinedDeep(value) {
|
|
1079
|
+
if (Array.isArray(value)) {
|
|
1080
|
+
return value.map((item) => pruneUndefinedDeep(item)).filter((item) => item !== void 0);
|
|
1081
|
+
}
|
|
1082
|
+
if (value && typeof value === "object") {
|
|
1083
|
+
const entries = Object.entries(value).filter(([, nestedValue]) => nestedValue !== void 0).map(([key, nestedValue]) => [key, pruneUndefinedDeep(nestedValue)]);
|
|
1084
|
+
return Object.fromEntries(entries);
|
|
1085
|
+
}
|
|
1086
|
+
return value;
|
|
1087
|
+
}
|
|
1088
|
+
function createSoapFaultError(fault) {
|
|
1089
|
+
const faultCode = typeof fault.faultcode === "string" ? fault.faultcode : getNestedString(fault, ["Code", "Value"]);
|
|
1090
|
+
const message = typeof fault.faultstring === "string" ? fault.faultstring : getNestedString(fault, ["Reason", "Text"]) ?? "ARCA SOAP fault response";
|
|
1091
|
+
return new ArcaSoapFaultError(message, {
|
|
1092
|
+
faultCode: faultCode ?? void 0,
|
|
1093
|
+
detail: fault
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
function getNestedString(value, path2) {
|
|
1097
|
+
let current = value;
|
|
1098
|
+
for (const key of path2) {
|
|
1099
|
+
if (!current || typeof current !== "object") {
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
current = current[key];
|
|
1103
|
+
}
|
|
1104
|
+
return typeof current === "string" ? current : null;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/soap/index.ts
|
|
1108
|
+
function createSoapTransport(options) {
|
|
1109
|
+
return {
|
|
1110
|
+
async execute(request) {
|
|
1111
|
+
const serviceConfig = getArcaServiceConfig(request.service);
|
|
1112
|
+
const soapActionOperation = request.operation;
|
|
1113
|
+
const bodyElementName = request.bodyElementName ?? request.operation;
|
|
1114
|
+
const soapAction = serviceConfig.usesEmptySoapAction ? "" : `${serviceConfig.soapActionBase}${soapActionOperation}`;
|
|
1115
|
+
const contentType = serviceConfig.soapVersion === "1.2" ? `application/soap+xml; charset=utf-8; action="${soapAction}"` : 'text/xml; charset="utf-8"';
|
|
1116
|
+
const xml = buildSoapEnvelope(
|
|
1117
|
+
serviceConfig.soapVersion,
|
|
1118
|
+
bodyElementName,
|
|
1119
|
+
serviceConfig.namespace,
|
|
1120
|
+
request.body,
|
|
1121
|
+
{
|
|
1122
|
+
namespaceMode: request.bodyElementNamespaceMode
|
|
1123
|
+
}
|
|
1124
|
+
);
|
|
1125
|
+
const responseXml = await postXml({
|
|
1126
|
+
url: serviceConfig.endpoint[options.config.environment],
|
|
1127
|
+
body: xml,
|
|
1128
|
+
contentType,
|
|
1129
|
+
soapAction: serviceConfig.soapVersion === "1.1" ? soapAction : void 0,
|
|
1130
|
+
useLegacyTlsSecurityLevel0: options.config.environment === "production" && serviceConfig.useLegacyTlsSecurityLevel0 === true
|
|
1131
|
+
});
|
|
1132
|
+
const soapBody = parseSoapBody(responseXml);
|
|
1133
|
+
const [, result] = getSingleBodyEntry(soapBody);
|
|
1134
|
+
return {
|
|
1135
|
+
service: request.service,
|
|
1136
|
+
operation: request.operation,
|
|
1137
|
+
raw: responseXml,
|
|
1138
|
+
result
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// src/wsaa/index.ts
|
|
1145
|
+
import { createHash } from "crypto";
|
|
1146
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1147
|
+
import path from "path";
|
|
1148
|
+
import forge from "node-forge";
|
|
1149
|
+
function createWsaaAuthModule(options) {
|
|
1150
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1151
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
1152
|
+
const persistedCache = createPersistedCredentialStore(options.config.wsaa?.cache);
|
|
1153
|
+
return {
|
|
1154
|
+
async login(service, authOptions = {}) {
|
|
1155
|
+
const cacheKey = buildWsaaCacheKey(options.config, service);
|
|
1156
|
+
const running = inFlight.get(cacheKey);
|
|
1157
|
+
if (running) {
|
|
1158
|
+
return running;
|
|
1159
|
+
}
|
|
1160
|
+
const loginPromise = (async () => {
|
|
1161
|
+
if (!authOptions.forceRefresh) {
|
|
1162
|
+
const cached = await getCachedCredentials(
|
|
1163
|
+
cache,
|
|
1164
|
+
cacheKey,
|
|
1165
|
+
persistedCache
|
|
1166
|
+
);
|
|
1167
|
+
if (cached) {
|
|
1168
|
+
return cached;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
const credentials = await requestCredentials(options.config, service);
|
|
1172
|
+
cache.set(cacheKey, credentials);
|
|
1173
|
+
await persistedCache.write(cacheKey, credentials);
|
|
1174
|
+
return credentials;
|
|
1175
|
+
})();
|
|
1176
|
+
inFlight.set(cacheKey, loginPromise);
|
|
1177
|
+
try {
|
|
1178
|
+
return await loginPromise;
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
if (!authOptions.forceRefresh && error instanceof ArcaSoapFaultError && error.faultCode === "ns1:coe.alreadyAuthenticated") {
|
|
1181
|
+
const cached = await getCachedCredentials(
|
|
1182
|
+
cache,
|
|
1183
|
+
cacheKey,
|
|
1184
|
+
persistedCache
|
|
1185
|
+
);
|
|
1186
|
+
if (cached) {
|
|
1187
|
+
return cached;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
throw error;
|
|
1191
|
+
} finally {
|
|
1192
|
+
inFlight.delete(cacheKey);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
async function requestCredentials(config, service) {
|
|
1198
|
+
const loginTicketRequestXml = buildLoginTicketRequest(service);
|
|
1199
|
+
const signedCms = signLoginTicketRequest(loginTicketRequestXml, {
|
|
1200
|
+
certificatePem: config.certificatePem,
|
|
1201
|
+
privateKeyPem: config.privateKeyPem
|
|
1202
|
+
});
|
|
1203
|
+
const requestXml = buildSoapEnvelope(
|
|
1204
|
+
ARCA_WSAA_CONFIG.soapVersion,
|
|
1205
|
+
"loginCms",
|
|
1206
|
+
ARCA_WSAA_CONFIG.namespace,
|
|
1207
|
+
{ in0: signedCms }
|
|
1208
|
+
);
|
|
1209
|
+
const responseXml = await postXml({
|
|
1210
|
+
url: ARCA_WSAA_CONFIG.endpoint[config.environment],
|
|
1211
|
+
body: requestXml,
|
|
1212
|
+
contentType: 'text/xml; charset="utf-8"',
|
|
1213
|
+
soapAction: ARCA_WSAA_CONFIG.soapActionBase
|
|
1214
|
+
});
|
|
1215
|
+
const soapBody = parseSoapBody(responseXml);
|
|
1216
|
+
const [, response] = getSingleBodyEntry(soapBody);
|
|
1217
|
+
const loginCmsReturn = response.loginCmsReturn;
|
|
1218
|
+
if (typeof loginCmsReturn !== "string" || loginCmsReturn.trim().length < 1) {
|
|
1219
|
+
throw new ArcaTransportError(
|
|
1220
|
+
"WSAA response did not include loginCmsReturn XML"
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
return parseLoginTicketResponse(loginCmsReturn);
|
|
1224
|
+
}
|
|
1225
|
+
function buildWsaaCacheKey(config, service) {
|
|
1226
|
+
return [config.environment, service, getCertificateFingerprint(config)].join(
|
|
1227
|
+
":"
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
function getCertificateFingerprint(config) {
|
|
1231
|
+
return createHash("sha256").update(config.certificatePem).digest("hex");
|
|
1232
|
+
}
|
|
1233
|
+
function isCredentialValid(credentials) {
|
|
1234
|
+
return new Date(credentials.expiresAt).getTime() - Date.now() > 6e4;
|
|
1235
|
+
}
|
|
1236
|
+
async function getCachedCredentials(cache, cacheKey, persistedCache) {
|
|
1237
|
+
const localCached = cache.get(cacheKey);
|
|
1238
|
+
if (localCached && isCredentialValid(localCached)) {
|
|
1239
|
+
return localCached;
|
|
1240
|
+
}
|
|
1241
|
+
const persisted = await persistedCache.read(cacheKey);
|
|
1242
|
+
if (persisted) {
|
|
1243
|
+
cache.set(cacheKey, persisted);
|
|
1244
|
+
return persisted;
|
|
1245
|
+
}
|
|
1246
|
+
return null;
|
|
1247
|
+
}
|
|
1248
|
+
function createPersistedCredentialStore(cacheConfig) {
|
|
1249
|
+
if (!cacheConfig || cacheConfig.mode !== "disk") {
|
|
1250
|
+
return {
|
|
1251
|
+
async read() {
|
|
1252
|
+
return null;
|
|
1253
|
+
},
|
|
1254
|
+
async write() {
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
const cacheDir = path.resolve(cacheConfig.directory);
|
|
1259
|
+
return {
|
|
1260
|
+
async read(cacheKey) {
|
|
1261
|
+
try {
|
|
1262
|
+
const serialized = await readFile(
|
|
1263
|
+
getSharedCacheFilePath(cacheDir, cacheKey),
|
|
1264
|
+
"utf8"
|
|
1265
|
+
);
|
|
1266
|
+
const parsed = JSON.parse(serialized);
|
|
1267
|
+
if (typeof parsed.token !== "string" || typeof parsed.sign !== "string" || typeof parsed.expiresAt !== "string") {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
const credentials = {
|
|
1271
|
+
token: parsed.token,
|
|
1272
|
+
sign: parsed.sign,
|
|
1273
|
+
expiresAt: parsed.expiresAt
|
|
1274
|
+
};
|
|
1275
|
+
return isCredentialValid(credentials) ? credentials : null;
|
|
1276
|
+
} catch {
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
async write(cacheKey, credentials) {
|
|
1281
|
+
await mkdir(cacheDir, { recursive: true });
|
|
1282
|
+
await writeFile(
|
|
1283
|
+
getSharedCacheFilePath(cacheDir, cacheKey),
|
|
1284
|
+
JSON.stringify(credentials),
|
|
1285
|
+
"utf8"
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
function getSharedCacheFilePath(cacheDir, cacheKey) {
|
|
1291
|
+
const fileName = `${createHash("sha256").update(cacheKey).digest("hex")}.json`;
|
|
1292
|
+
return path.join(cacheDir, fileName);
|
|
1293
|
+
}
|
|
1294
|
+
function buildLoginTicketRequest(service) {
|
|
1295
|
+
const uniqueId = Math.floor(Date.now() / 1e3);
|
|
1296
|
+
const generationTime = new Date(Date.now() - 5 * 6e4).toISOString().replace(".000Z", "Z");
|
|
1297
|
+
const expirationTime = new Date(Date.now() + 5 * 6e4).toISOString().replace(".000Z", "Z");
|
|
1298
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1299
|
+
<loginTicketRequest version="1.0">
|
|
1300
|
+
<header>
|
|
1301
|
+
<uniqueId>${uniqueId}</uniqueId>
|
|
1302
|
+
<generationTime>${generationTime}</generationTime>
|
|
1303
|
+
<expirationTime>${expirationTime}</expirationTime>
|
|
1304
|
+
</header>
|
|
1305
|
+
<service>${service}</service>
|
|
1306
|
+
</loginTicketRequest>`;
|
|
1307
|
+
}
|
|
1308
|
+
function signLoginTicketRequest(loginTicketRequestXml, options) {
|
|
1309
|
+
const certificate = forge.pki.certificateFromPem(options.certificatePem);
|
|
1310
|
+
const privateKey = forge.pki.privateKeyFromPem(options.privateKeyPem);
|
|
1311
|
+
const signedData = forge.pkcs7.createSignedData();
|
|
1312
|
+
signedData.content = forge.util.createBuffer(loginTicketRequestXml, "utf8");
|
|
1313
|
+
signedData.addCertificate(certificate);
|
|
1314
|
+
const authenticatedAttributes = [
|
|
1315
|
+
{
|
|
1316
|
+
type: String(forge.pki.oids.contentType),
|
|
1317
|
+
value: String(forge.pki.oids.data)
|
|
1318
|
+
},
|
|
1319
|
+
{
|
|
1320
|
+
type: String(forge.pki.oids.messageDigest)
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
type: String(forge.pki.oids.signingTime),
|
|
1324
|
+
value: /* @__PURE__ */ new Date()
|
|
1325
|
+
}
|
|
1326
|
+
];
|
|
1327
|
+
const signerOptions = {
|
|
1328
|
+
key: privateKey,
|
|
1329
|
+
certificate,
|
|
1330
|
+
digestAlgorithm: String(forge.pki.oids.sha1),
|
|
1331
|
+
authenticatedAttributes
|
|
1332
|
+
};
|
|
1333
|
+
signedData.addSigner(signerOptions);
|
|
1334
|
+
signedData.sign();
|
|
1335
|
+
const der = forge.asn1.toDer(signedData.toAsn1()).getBytes();
|
|
1336
|
+
return Buffer.from(der, "binary").toString("base64");
|
|
1337
|
+
}
|
|
1338
|
+
function parseLoginTicketResponse(xml) {
|
|
1339
|
+
const parsed = parseXmlDocument(xml);
|
|
1340
|
+
const response = parsed.loginTicketResponse ?? parsed;
|
|
1341
|
+
const header = response.header;
|
|
1342
|
+
const credentials = response.credentials;
|
|
1343
|
+
const token = credentials?.token;
|
|
1344
|
+
const sign = credentials?.sign;
|
|
1345
|
+
const expiresAt = header?.expirationTime;
|
|
1346
|
+
if (typeof token !== "string" || typeof sign !== "string" || typeof expiresAt !== "string") {
|
|
1347
|
+
throw new ArcaTransportError(
|
|
1348
|
+
"Invalid WSAA login ticket response structure"
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
return {
|
|
1352
|
+
token,
|
|
1353
|
+
sign,
|
|
1354
|
+
expiresAt
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// src/client.ts
|
|
1359
|
+
function createArcaClient(config) {
|
|
1360
|
+
assertArcaClientConfig(config);
|
|
1361
|
+
const normalizedConfig = normalizeArcaClientConfig(config);
|
|
1362
|
+
const auth = createWsaaAuthModule({ config: normalizedConfig });
|
|
1363
|
+
const soap = createSoapTransport({ config: normalizedConfig });
|
|
1364
|
+
return {
|
|
1365
|
+
config: normalizedConfig,
|
|
1366
|
+
wsfe: createWsfeService({ config: normalizedConfig, auth, soap }),
|
|
1367
|
+
wsmtxca: createWsmtxcaService({ config: normalizedConfig, auth, soap }),
|
|
1368
|
+
padron: createPadronService({ config: normalizedConfig, auth, soap })
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
export {
|
|
1372
|
+
ARCA_ENVIRONMENTS,
|
|
1373
|
+
ARCA_ENV_VARIABLES,
|
|
1374
|
+
ArcaConfigurationError,
|
|
1375
|
+
ArcaError,
|
|
1376
|
+
ArcaServiceError,
|
|
1377
|
+
ArcaSoapFaultError,
|
|
1378
|
+
ArcaTransportError,
|
|
1379
|
+
assertArcaClientConfig,
|
|
1380
|
+
createArcaClient,
|
|
1381
|
+
createArcaClientConfigFromEnv,
|
|
1382
|
+
createPadronService,
|
|
1383
|
+
createWsfeService,
|
|
1384
|
+
createWsmtxcaService,
|
|
1385
|
+
resolveArcaEnvironment
|
|
1386
|
+
};
|
|
1387
|
+
//# sourceMappingURL=index.js.map
|