@deuriib/dgii-ecf 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +69 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +70 -79
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -668,22 +668,6 @@ var C14N_INCLUSIVE = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
|
|
|
668
668
|
var RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
|
|
669
669
|
var SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256";
|
|
670
670
|
var ENVELOPED = "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
|
|
671
|
-
var CUSTOM_DIGEST_URI = "http://dgii.gov.do/digest/sha256-c14n";
|
|
672
|
-
var DgiiDigest = class {
|
|
673
|
-
getHash(xml) {
|
|
674
|
-
const doc = new import_xmldom.DOMParser().parseFromString(
|
|
675
|
-
xml
|
|
676
|
-
);
|
|
677
|
-
const canonical = new import_xml_crypto.C14nCanonicalization().process(
|
|
678
|
-
doc,
|
|
679
|
-
{}
|
|
680
|
-
);
|
|
681
|
-
return import_node_crypto.default.createHash("sha256").update(canonical, "utf8").digest("base64");
|
|
682
|
-
}
|
|
683
|
-
getAlgorithmName() {
|
|
684
|
-
return SHA256;
|
|
685
|
-
}
|
|
686
|
-
};
|
|
687
671
|
var parseStrict = (xml) => {
|
|
688
672
|
const errors = [];
|
|
689
673
|
const doc = new import_xmldom.DOMParser({
|
|
@@ -752,12 +736,10 @@ var Signature = class {
|
|
|
752
736
|
signatureAlgorithm: RSA_SHA256,
|
|
753
737
|
canonicalizationAlgorithm: C14N_INCLUSIVE
|
|
754
738
|
});
|
|
755
|
-
const hashAlgorithms = sig.HashAlgorithms;
|
|
756
|
-
hashAlgorithms[CUSTOM_DIGEST_URI] = DgiiDigest;
|
|
757
739
|
sig.addReference({
|
|
758
740
|
xpath: `//*[local-name(.)='${elementName}']`,
|
|
759
741
|
transforms: [ENVELOPED],
|
|
760
|
-
digestAlgorithm:
|
|
742
|
+
digestAlgorithm: SHA256,
|
|
761
743
|
isEmptyUri: true
|
|
762
744
|
});
|
|
763
745
|
cleanNodes(doc);
|
|
@@ -1086,8 +1068,39 @@ var ECF = class {
|
|
|
1086
1068
|
}
|
|
1087
1069
|
buildIdempotencyKey(signedXml) {
|
|
1088
1070
|
const rnc = extractElementText(signedXml, "RNCEmisor") ?? "unknown-rnc";
|
|
1089
|
-
const
|
|
1090
|
-
return `${rnc}:${
|
|
1071
|
+
const ncf = extractElementText(signedXml, "eNCF") ?? extractElementText(signedXml, "RangoNCF") ?? "unknown-ncf";
|
|
1072
|
+
return `${rnc}:${ncf}:${sha256Hex(signedXml)}`;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Run a submission through the persist-before-transmit idempotency flow.
|
|
1076
|
+
*
|
|
1077
|
+
* The pending marker is written first so an interrupted send can be
|
|
1078
|
+
* reconciled instead of re-issued; a successful response replaces it, and
|
|
1079
|
+
* a failure clears it so the caller can retry.
|
|
1080
|
+
*/
|
|
1081
|
+
async submitIdempotent(key, send) {
|
|
1082
|
+
const cached = await this.idempotency.get(key);
|
|
1083
|
+
if (cached !== void 0) {
|
|
1084
|
+
if (isPendingSubmission(cached)) {
|
|
1085
|
+
return (0, import_neverthrow.err)(
|
|
1086
|
+
new DgiiError(
|
|
1087
|
+
"A submission with this key is already pending; reconcile via a status lookup before retrying",
|
|
1088
|
+
{ code: "IDEMPOTENCY" }
|
|
1089
|
+
)
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
return (0, import_neverthrow.ok)(cached);
|
|
1093
|
+
}
|
|
1094
|
+
return this.singleFlight(key, async () => {
|
|
1095
|
+
await this.idempotency.set(key, PENDING_SUBMISSION);
|
|
1096
|
+
const result = await send();
|
|
1097
|
+
if (result.isOk()) {
|
|
1098
|
+
await this.idempotency.set(key, result.value);
|
|
1099
|
+
} else {
|
|
1100
|
+
await this.idempotency.delete(key);
|
|
1101
|
+
}
|
|
1102
|
+
return result;
|
|
1103
|
+
});
|
|
1091
1104
|
}
|
|
1092
1105
|
async singleFlight(key, task) {
|
|
1093
1106
|
const existing = this.inFlight.get(key);
|
|
@@ -1160,66 +1173,28 @@ var ECF = class {
|
|
|
1160
1173
|
*/
|
|
1161
1174
|
async sendElectronicDocument(signedXml, fileName, buyerHost) {
|
|
1162
1175
|
const key = this.buildIdempotencyKey(signedXml);
|
|
1163
|
-
|
|
1164
|
-
if (cached !== void 0) {
|
|
1165
|
-
if (isPendingSubmission(cached)) {
|
|
1166
|
-
return (0, import_neverthrow.err)(
|
|
1167
|
-
new DgiiError(
|
|
1168
|
-
"A submission with this key is already pending; reconcile via a status lookup before retrying",
|
|
1169
|
-
{ code: "IDEMPOTENCY" }
|
|
1170
|
-
)
|
|
1171
|
-
);
|
|
1172
|
-
}
|
|
1173
|
-
return (0, import_neverthrow.ok)(cached);
|
|
1174
|
-
}
|
|
1175
|
-
return this.singleFlight(key, async () => {
|
|
1176
|
-
await this.idempotency.set(key, PENDING_SUBMISSION);
|
|
1176
|
+
return this.submitIdempotent(key, () => {
|
|
1177
1177
|
const url = buyerHost ? buildSenderReceiverUrl(buyerHost, "fe/recepcion/api/ecf" /* SendInvoice */) : buildUrl(this.environment, "recepcion/api/FacturasElectronicas" /* SendInvoice */);
|
|
1178
|
-
|
|
1178
|
+
return this.http.postMultipart(
|
|
1179
1179
|
url,
|
|
1180
1180
|
fileName,
|
|
1181
1181
|
signedXml,
|
|
1182
1182
|
{ absoluteUrl: true, resource: "recepcion/api/FacturasElectronicas" /* SendInvoice */ }
|
|
1183
1183
|
);
|
|
1184
|
-
if (result.isOk()) {
|
|
1185
|
-
await this.idempotency.set(key, result.value);
|
|
1186
|
-
} else {
|
|
1187
|
-
await this.idempotency.delete(key);
|
|
1188
|
-
}
|
|
1189
|
-
return result;
|
|
1190
1184
|
});
|
|
1191
1185
|
}
|
|
1192
1186
|
/** Submit a signed summary invoice (RFCE). Idempotent like submissions. */
|
|
1193
1187
|
async sendSummary(signedXml, fileName) {
|
|
1194
1188
|
const key = this.buildIdempotencyKey(signedXml);
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
new DgiiError(
|
|
1200
|
-
"A submission with this key is already pending; reconcile via a status lookup before retrying",
|
|
1201
|
-
{ code: "IDEMPOTENCY" }
|
|
1202
|
-
)
|
|
1203
|
-
);
|
|
1204
|
-
}
|
|
1205
|
-
return (0, import_neverthrow.ok)(cached);
|
|
1206
|
-
}
|
|
1207
|
-
return this.singleFlight(key, async () => {
|
|
1208
|
-
await this.idempotency.set(key, PENDING_SUBMISSION);
|
|
1209
|
-
const url = buildUrl(this.environment, "recepcionfc/api/recepcion/ecf" /* SendSummary */, BaseUrl.CF);
|
|
1210
|
-
const result = await this.http.postMultipart(
|
|
1211
|
-
url,
|
|
1189
|
+
return this.submitIdempotent(
|
|
1190
|
+
key,
|
|
1191
|
+
() => this.http.postMultipart(
|
|
1192
|
+
buildUrl(this.environment, "recepcionfc/api/recepcion/ecf" /* SendSummary */, BaseUrl.CF),
|
|
1212
1193
|
fileName,
|
|
1213
1194
|
signedXml,
|
|
1214
1195
|
{ absoluteUrl: true, resource: "recepcionfc/api/recepcion/ecf" /* SendSummary */ }
|
|
1215
|
-
)
|
|
1216
|
-
|
|
1217
|
-
await this.idempotency.set(key, result.value);
|
|
1218
|
-
} else {
|
|
1219
|
-
await this.idempotency.delete(key);
|
|
1220
|
-
}
|
|
1221
|
-
return result;
|
|
1222
|
-
});
|
|
1196
|
+
)
|
|
1197
|
+
);
|
|
1223
1198
|
}
|
|
1224
1199
|
statusTrackId(trackId) {
|
|
1225
1200
|
const url = buildUrl(this.environment, "consultaresultado/api/Consultas/Estado" /* TrackResultStatus */);
|
|
@@ -1279,21 +1254,37 @@ var ECF = class {
|
|
|
1279
1254
|
{ absoluteUrl: true, resource: "consultarfce/api/Consultas/Consulta" /* InquiryInvoiceSummary */ }
|
|
1280
1255
|
);
|
|
1281
1256
|
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Submit a signed commercial approval (ACECF). Idempotent like submissions,
|
|
1259
|
+
* keyed on `RNC + eNCF + SHA-256(signed XML)`.
|
|
1260
|
+
*/
|
|
1282
1261
|
sendCommercialApproval(signedXml, fileName) {
|
|
1283
|
-
const
|
|
1284
|
-
return this.
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1262
|
+
const key = this.buildIdempotencyKey(signedXml);
|
|
1263
|
+
return this.submitIdempotent(
|
|
1264
|
+
key,
|
|
1265
|
+
() => this.http.postMultipart(
|
|
1266
|
+
buildUrl(this.environment, "aprobacionComercial/api/AprobacionComercial" /* CommercialApproval */),
|
|
1267
|
+
fileName,
|
|
1268
|
+
signedXml,
|
|
1269
|
+
{ absoluteUrl: true, resource: "aprobacionComercial/api/AprobacionComercial" /* CommercialApproval */ }
|
|
1270
|
+
)
|
|
1289
1271
|
);
|
|
1290
1272
|
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Submit a signed range-voiding request (ANECF). Idempotent like
|
|
1275
|
+
* submissions, keyed on `RNC + RangoNCF + SHA-256(signed XML)`.
|
|
1276
|
+
*/
|
|
1291
1277
|
voidENCF(signedXml, fileName) {
|
|
1292
|
-
const
|
|
1293
|
-
return this.
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1278
|
+
const key = this.buildIdempotencyKey(signedXml);
|
|
1279
|
+
return this.submitIdempotent(
|
|
1280
|
+
key,
|
|
1281
|
+
() => this.http.postMultipart(
|
|
1282
|
+
buildUrl(this.environment, "anulacionrangos/api/operaciones/anularrango" /* Void */),
|
|
1283
|
+
fileName,
|
|
1284
|
+
signedXml,
|
|
1285
|
+
{ absoluteUrl: true, resource: "anulacionrangos/api/operaciones/anularrango" /* Void */ }
|
|
1286
|
+
)
|
|
1287
|
+
);
|
|
1297
1288
|
}
|
|
1298
1289
|
};
|
|
1299
1290
|
// Annotate the CommonJS export names for ESM import in node:
|