@taxora/sdk 1.2.0 → 1.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.cjs CHANGED
@@ -26,9 +26,11 @@ __export(index_exports, {
26
26
  AuthenticationException: () => AuthenticationException,
27
27
  CompanyAddress: () => CompanyAddress,
28
28
  CompanyEndpoint: () => CompanyEndpoint,
29
+ ComplianceDocumentType: () => ComplianceDocumentType,
29
30
  ComplianceEnrollment: () => ComplianceEnrollment,
30
31
  ComplianceEnrollmentPage: () => ComplianceEnrollmentPage,
31
32
  ComplianceEnrollmentStatus: () => ComplianceEnrollmentStatus,
33
+ ComplianceService: () => ComplianceService,
32
34
  ComplianceTaxReport: () => ComplianceTaxReport,
33
35
  ComplianceTaxReportPage: () => ComplianceTaxReportPage,
34
36
  ComplianceTaxReportState: () => ComplianceTaxReportState,
@@ -36,6 +38,7 @@ __export(index_exports, {
36
38
  ComplianceTransactionPage: () => ComplianceTransactionPage,
37
39
  ComplianceTransactionState: () => ComplianceTransactionState,
38
40
  ComplianceTransactionType: () => ComplianceTransactionType,
41
+ DEFAULT_WEBHOOK_TOLERANCE_SECONDS: () => DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
39
42
  EReportingEndpoint: () => EReportingEndpoint,
40
43
  Environment: () => Environment,
41
44
  FetchHttpClient: () => FetchHttpClient,
@@ -44,8 +47,12 @@ __export(index_exports, {
44
47
  InMemoryTokenStorage: () => InMemoryTokenStorage,
45
48
  Language: () => Language,
46
49
  LoginIdentifier: () => LoginIdentifier,
50
+ NorwegianOrgNumber: () => NorwegianOrgNumber,
51
+ PeppolLookupResult: () => PeppolLookupResult,
52
+ PeppolLookupStatus: () => PeppolLookupStatus,
47
53
  ProviderDocument: () => ProviderDocument,
48
54
  ProviderDocumentLine: () => ProviderDocumentLine,
55
+ RegistryCompany: () => RegistryCompany,
49
56
  RetryPolicy: () => RetryPolicy,
50
57
  RevenueStatistics: () => RevenueStatistics,
51
58
  ScoreBreakdown: () => ScoreBreakdown,
@@ -55,6 +62,7 @@ __export(index_exports, {
55
62
  SmartEnrichmentHistoryRow: () => SmartEnrichmentHistoryRow,
56
63
  SmartEnrichmentJob: () => SmartEnrichmentJob,
57
64
  SmartEnrichmentMode: () => SmartEnrichmentMode,
65
+ SmartEnrichmentRejectionReason: () => SmartEnrichmentRejectionReason,
58
66
  SmartEnrichmentResource: () => SmartEnrichmentResource,
59
67
  SmartEnrichmentStatistics: () => SmartEnrichmentStatistics,
60
68
  SmartEnrichmentStatus: () => SmartEnrichmentStatus,
@@ -70,22 +78,38 @@ __export(index_exports, {
70
78
  VatResource: () => VatResource,
71
79
  VatState: () => VatState,
72
80
  VatValidationAddressInput: () => VatValidationAddressInput,
81
+ WEBHOOK_ID_HEADER: () => WEBHOOK_ID_HEADER,
82
+ WEBHOOK_SIGNATURE_HEADER: () => WEBHOOK_SIGNATURE_HEADER,
83
+ WebhookSignatureVerificationError: () => WebhookSignatureVerificationError,
84
+ computeWebhookSignature: () => computeWebhookSignature,
85
+ constructWebhookEvent: () => constructWebhookEvent,
73
86
  describeApiError: () => describeApiError,
87
+ describeComplianceDocumentType: () => describeComplianceDocumentType,
74
88
  describeComplianceEnrollmentStatus: () => describeComplianceEnrollmentStatus,
89
+ describeComplianceService: () => describeComplianceService,
75
90
  describeComplianceTaxReportState: () => describeComplianceTaxReportState,
76
91
  describeComplianceTransactionState: () => describeComplianceTransactionState,
77
92
  describeComplianceTransactionType: () => describeComplianceTransactionType,
93
+ describePeppolLookupStatus: () => describePeppolLookupStatus,
78
94
  describeSmartEnrichmentMode: () => describeSmartEnrichmentMode,
95
+ describeSmartEnrichmentRejectionReason: () => describeSmartEnrichmentRejectionReason,
79
96
  describeSmartEnrichmentStatus: () => describeSmartEnrichmentStatus,
80
97
  describeVatState: () => describeVatState,
98
+ generateWebhookSignatureHeader: () => generateWebhookSignatureHeader,
81
99
  getBaseUrl: () => getBaseUrl,
82
100
  getFailedVatStates: () => getFailedVatStates,
101
+ parseWebhookSignatureHeader: () => parseWebhookSignatureHeader,
102
+ toComplianceDocumentType: () => toComplianceDocumentType,
83
103
  toComplianceEnrollmentStatus: () => toComplianceEnrollmentStatus,
104
+ toComplianceService: () => toComplianceService,
84
105
  toComplianceTaxReportState: () => toComplianceTaxReportState,
85
106
  toComplianceTransactionState: () => toComplianceTransactionState,
86
107
  toComplianceTransactionType: () => toComplianceTransactionType,
108
+ toPeppolLookupStatus: () => toPeppolLookupStatus,
87
109
  toSmartEnrichmentMode: () => toSmartEnrichmentMode,
88
- toSmartEnrichmentStatus: () => toSmartEnrichmentStatus
110
+ toSmartEnrichmentRejectionReason: () => toSmartEnrichmentRejectionReason,
111
+ toSmartEnrichmentStatus: () => toSmartEnrichmentStatus,
112
+ verifyWebhookSignature: () => verifyWebhookSignature
89
113
  });
90
114
  module.exports = __toCommonJS(index_exports);
91
115
 
@@ -414,13 +438,7 @@ async function withResponseRetries(policy, operation, send) {
414
438
  if (!policy.shouldRetry(response.status, attempt, retryAfter)) {
415
439
  if (attempt > 1 && policy.isRetryable(response.status, retryAfter)) {
416
440
  const body = await response.text();
417
- const last = new HttpException(
418
- describeApiError(body, response.status),
419
- response.status,
420
- body,
421
- {},
422
- retryAfter
423
- );
441
+ const last = new HttpException(describeApiError(body, response.status), response.status, body, {}, retryAfter);
424
442
  throw HttpException.afterAttempts(operation, attempt, last);
425
443
  }
426
444
  return response;
@@ -637,6 +655,31 @@ function describeComplianceEnrollmentStatus(status) {
637
655
  return DESCRIPTIONS[status];
638
656
  }
639
657
 
658
+ // src/enums/ComplianceService.ts
659
+ var ComplianceService = {
660
+ E_REPORTING: "e_reporting",
661
+ E_INVOICING: "e_invoicing",
662
+ UNKNOWN: "unknown"
663
+ };
664
+ var VALUES2 = Object.values(ComplianceService);
665
+ function toComplianceService(value) {
666
+ if (typeof value === "string") {
667
+ const normalized = value.trim().toLowerCase();
668
+ if (VALUES2.includes(normalized)) {
669
+ return normalized;
670
+ }
671
+ }
672
+ return ComplianceService.UNKNOWN;
673
+ }
674
+ var DESCRIPTIONS2 = {
675
+ [ComplianceService.E_REPORTING]: "E-reporting of transaction data to the tax authority (France: DGFiP Flux 10).",
676
+ [ComplianceService.E_INVOICING]: "Structured e-invoices delivered to the buyer (Norway: EHF / Peppol BIS 3.0).",
677
+ [ComplianceService.UNKNOWN]: "Unknown service."
678
+ };
679
+ function describeComplianceService(service) {
680
+ return DESCRIPTIONS2[service];
681
+ }
682
+
640
683
  // src/dto/ComplianceEnrollment.ts
641
684
  function asString(value, fallback = "") {
642
685
  return typeof value === "string" ? value : fallback;
@@ -648,7 +691,7 @@ function asNumber(value) {
648
691
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
649
692
  }
650
693
  var ComplianceEnrollment = class _ComplianceEnrollment {
651
- constructor(id, companyId, country, regime, provider, status, statusLabel, statusError, providerAccountId, taxId, companyRegisterId, companyRegisterScheme, regimeConfig, reportingStartDate, notificationEmail, autoSend, createdAt, updatedAt) {
694
+ constructor(id, companyId, country, regime, provider, status, statusLabel, statusError, providerAccountId, taxId, companyRegisterId, companyRegisterScheme, regimeConfig, reportingStartDate, notificationEmail, autoSend, createdAt, updatedAt, service = ComplianceService.UNKNOWN) {
652
695
  this.id = id;
653
696
  this.companyId = companyId;
654
697
  this.country = country;
@@ -667,6 +710,7 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
667
710
  this.autoSend = autoSend;
668
711
  this.createdAt = createdAt;
669
712
  this.updatedAt = updatedAt;
713
+ this.service = service;
670
714
  }
671
715
  id;
672
716
  companyId;
@@ -686,6 +730,7 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
686
730
  autoSend;
687
731
  createdAt;
688
732
  updatedAt;
733
+ service;
689
734
  static fromArray(data) {
690
735
  const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
691
736
  return new _ComplianceEnrollment(
@@ -706,7 +751,8 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
706
751
  asNullableString(data["notification_email"]),
707
752
  data["auto_send"] === true,
708
753
  asNullableString(data["created_at"]),
709
- asNullableString(data["updated_at"])
754
+ asNullableString(data["updated_at"]),
755
+ toComplianceService(data["service"])
710
756
  );
711
757
  }
712
758
  };
@@ -756,17 +802,17 @@ var ComplianceTaxReportState = {
756
802
  ERROR: "error",
757
803
  UNKNOWN: "unknown"
758
804
  };
759
- var VALUES2 = Object.values(ComplianceTaxReportState);
805
+ var VALUES3 = Object.values(ComplianceTaxReportState);
760
806
  function toComplianceTaxReportState(value) {
761
807
  if (typeof value === "string") {
762
808
  const normalized = value.trim().toLowerCase();
763
- if (VALUES2.includes(normalized)) {
809
+ if (VALUES3.includes(normalized)) {
764
810
  return normalized;
765
811
  }
766
812
  }
767
813
  return ComplianceTaxReportState.UNKNOWN;
768
814
  }
769
- var DESCRIPTIONS2 = {
815
+ var DESCRIPTIONS3 = {
770
816
  [ComplianceTaxReportState.NEW]: "Created at provider, waiting for the daily ledger batch.",
771
817
  [ComplianceTaxReportState.SENT]: "Ledger transmitted to PPF.",
772
818
  [ComplianceTaxReportState.ACKNOWLEDGED]: "PPF acknowledged receipt of the ledger.",
@@ -776,7 +822,7 @@ var DESCRIPTIONS2 = {
776
822
  [ComplianceTaxReportState.UNKNOWN]: "Unknown state."
777
823
  };
778
824
  function describeComplianceTaxReportState(state) {
779
- return DESCRIPTIONS2[state];
825
+ return DESCRIPTIONS3[state];
780
826
  }
781
827
 
782
828
  // src/dto/ComplianceTaxReport.ts
@@ -898,6 +944,31 @@ var ComplianceTaxReportPage = class _ComplianceTaxReportPage {
898
944
  }
899
945
  };
900
946
 
947
+ // src/enums/ComplianceDocumentType.ts
948
+ var ComplianceDocumentType = {
949
+ INVOICE: "invoice",
950
+ CREDIT_NOTE: "credit_note",
951
+ UNKNOWN: "unknown"
952
+ };
953
+ var VALUES4 = Object.values(ComplianceDocumentType);
954
+ function toComplianceDocumentType(value) {
955
+ if (typeof value === "string") {
956
+ const normalized = value.trim().toLowerCase();
957
+ if (VALUES4.includes(normalized)) {
958
+ return normalized;
959
+ }
960
+ }
961
+ return ComplianceDocumentType.UNKNOWN;
962
+ }
963
+ var DESCRIPTIONS4 = {
964
+ [ComplianceDocumentType.INVOICE]: "Commercial invoice.",
965
+ [ComplianceDocumentType.CREDIT_NOTE]: "Credit note correcting a previously issued invoice.",
966
+ [ComplianceDocumentType.UNKNOWN]: "Unknown document type."
967
+ };
968
+ function describeComplianceDocumentType(type) {
969
+ return DESCRIPTIONS4[type];
970
+ }
971
+
901
972
  // src/enums/ComplianceTransactionState.ts
902
973
  var ComplianceTransactionState = {
903
974
  PENDING: "pending",
@@ -906,17 +977,17 @@ var ComplianceTransactionState = {
906
977
  ERROR: "error",
907
978
  UNKNOWN: "unknown"
908
979
  };
909
- var VALUES3 = Object.values(ComplianceTransactionState);
980
+ var VALUES5 = Object.values(ComplianceTransactionState);
910
981
  function toComplianceTransactionState(value) {
911
982
  if (typeof value === "string") {
912
983
  const normalized = value.trim().toLowerCase();
913
- if (VALUES3.includes(normalized)) {
984
+ if (VALUES5.includes(normalized)) {
914
985
  return normalized;
915
986
  }
916
987
  }
917
988
  return ComplianceTransactionState.UNKNOWN;
918
989
  }
919
- var DESCRIPTIONS3 = {
990
+ var DESCRIPTIONS5 = {
920
991
  [ComplianceTransactionState.PENDING]: "Recorded locally, not yet submitted to the provider.",
921
992
  [ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
922
993
  [ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
@@ -924,7 +995,7 @@ var DESCRIPTIONS3 = {
924
995
  [ComplianceTransactionState.UNKNOWN]: "Unknown state."
925
996
  };
926
997
  function describeComplianceTransactionState(state) {
927
- return DESCRIPTIONS3[state];
998
+ return DESCRIPTIONS5[state];
928
999
  }
929
1000
 
930
1001
  // src/enums/ComplianceTransactionType.ts
@@ -936,17 +1007,17 @@ var ComplianceTransactionType = {
936
1007
  CROSSBORDER_INBOUND: "crossborder_inbound",
937
1008
  UNKNOWN: "unknown"
938
1009
  };
939
- var VALUES4 = Object.values(ComplianceTransactionType);
1010
+ var VALUES6 = Object.values(ComplianceTransactionType);
940
1011
  function toComplianceTransactionType(value) {
941
1012
  if (typeof value === "string") {
942
1013
  const normalized = value.trim().toLowerCase();
943
- if (VALUES4.includes(normalized)) {
1014
+ if (VALUES6.includes(normalized)) {
944
1015
  return normalized;
945
1016
  }
946
1017
  }
947
1018
  return ComplianceTransactionType.UNKNOWN;
948
1019
  }
949
- var DESCRIPTIONS4 = {
1020
+ var DESCRIPTIONS6 = {
950
1021
  [ComplianceTransactionType.B2C_OUTBOUND]: "Sale to a private person in the merchant country (DGFiP Flux 10.3).",
951
1022
  [ComplianceTransactionType.B2B_DOMESTIC_OUTBOUND]: "Sale to a domestic business (DGFiP Flux 1, Phase 2).",
952
1023
  [ComplianceTransactionType.B2B_DOMESTIC_INBOUND]: "Purchase from a domestic business (DGFiP Flux 1, Phase 2).",
@@ -955,7 +1026,7 @@ var DESCRIPTIONS4 = {
955
1026
  [ComplianceTransactionType.UNKNOWN]: "Unknown transaction type."
956
1027
  };
957
1028
  function describeComplianceTransactionType(type) {
958
- return DESCRIPTIONS4[type];
1029
+ return DESCRIPTIONS6[type];
959
1030
  }
960
1031
 
961
1032
  // src/dto/ComplianceTransaction.ts
@@ -973,8 +1044,34 @@ function asMoney2(value) {
973
1044
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
974
1045
  return "0.00";
975
1046
  }
1047
+ function asNullableNumber2(value) {
1048
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1049
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
1050
+ return null;
1051
+ }
1052
+ function asRecord(value) {
1053
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
1054
+ }
1055
+ function asRecordList(value) {
1056
+ return Array.isArray(value) ? value.filter((row) => asRecord(row) !== null) : [];
1057
+ }
1058
+ function toInvoiceLines(providerPayload) {
1059
+ const invoice = asRecord(asRecord(providerPayload)?.["invoice"]);
1060
+ return asRecordList(invoice?.["invoice_lines_attributes"]).map((line) => ({
1061
+ description: asString3(line["description"]),
1062
+ quantity: asNullableNumber2(line["quantity"]),
1063
+ price: asMoney2(line["price"]),
1064
+ unit: asNullableNumber2(line["unit"]),
1065
+ taxes: asRecordList(line["taxes_attributes"]).map((tax) => ({
1066
+ name: asString3(tax["name"]),
1067
+ percent: asNullableNumber2(tax["percent"]),
1068
+ category: asNullableString3(tax["category"]),
1069
+ comment: asNullableString3(tax["comment"])
1070
+ }))
1071
+ }));
1072
+ }
976
1073
  var ComplianceTransaction = class _ComplianceTransaction {
977
- constructor(id, companyId, complianceEnrollmentId, country, regime, transactionType, transactionTypeLabel, state, stateLabel, invoiceNumber, invoiceDate, dueDate, currency, subtotal, taxAmount, total, counterpartyName, counterpartyCountry, counterpartyVatNumber, isPaid, paidAt, providerInvoiceId, submissionError, reportedAt, taxReport, createdAt, updatedAt) {
1074
+ constructor(id, companyId, complianceEnrollmentId, country, regime, transactionType, transactionTypeLabel, state, stateLabel, invoiceNumber, invoiceDate, dueDate, currency, subtotal, taxAmount, total, counterpartyName, counterpartyCountry, counterpartyVatNumber, isPaid, paidAt, providerInvoiceId, submissionError, reportedAt, taxReport, createdAt, updatedAt, counterpartyRegisterId = null, counterpartyAddress = null, counterpartyCity = null, counterpartyPostalcode = null, counterpartyEmail = null, buyerReference = null, documentType = ComplianceDocumentType.INVOICE, amendedNumber = null, amendedDate = null, providerState = null, invoiceLines = []) {
978
1075
  this.id = id;
979
1076
  this.companyId = companyId;
980
1077
  this.complianceEnrollmentId = complianceEnrollmentId;
@@ -1002,6 +1099,17 @@ var ComplianceTransaction = class _ComplianceTransaction {
1002
1099
  this.taxReport = taxReport;
1003
1100
  this.createdAt = createdAt;
1004
1101
  this.updatedAt = updatedAt;
1102
+ this.counterpartyRegisterId = counterpartyRegisterId;
1103
+ this.counterpartyAddress = counterpartyAddress;
1104
+ this.counterpartyCity = counterpartyCity;
1105
+ this.counterpartyPostalcode = counterpartyPostalcode;
1106
+ this.counterpartyEmail = counterpartyEmail;
1107
+ this.buyerReference = buyerReference;
1108
+ this.documentType = documentType;
1109
+ this.amendedNumber = amendedNumber;
1110
+ this.amendedDate = amendedDate;
1111
+ this.providerState = providerState;
1112
+ this.invoiceLines = invoiceLines;
1005
1113
  }
1006
1114
  id;
1007
1115
  companyId;
@@ -1030,6 +1138,20 @@ var ComplianceTransaction = class _ComplianceTransaction {
1030
1138
  taxReport;
1031
1139
  createdAt;
1032
1140
  updatedAt;
1141
+ counterpartyRegisterId;
1142
+ counterpartyAddress;
1143
+ counterpartyCity;
1144
+ counterpartyPostalcode;
1145
+ counterpartyEmail;
1146
+ buyerReference;
1147
+ documentType;
1148
+ amendedNumber;
1149
+ amendedDate;
1150
+ providerState;
1151
+ invoiceLines;
1152
+ get isCreditNote() {
1153
+ return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
1154
+ }
1033
1155
  static fromArray(data) {
1034
1156
  const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
1035
1157
  return new _ComplianceTransaction(
@@ -1059,7 +1181,18 @@ var ComplianceTransaction = class _ComplianceTransaction {
1059
1181
  asNullableString3(data["reported_at"]),
1060
1182
  taxReport,
1061
1183
  asNullableString3(data["created_at"]),
1062
- asNullableString3(data["updated_at"])
1184
+ asNullableString3(data["updated_at"]),
1185
+ asNullableString3(data["counterparty_register_id"]),
1186
+ asNullableString3(data["counterparty_address"]),
1187
+ asNullableString3(data["counterparty_city"]),
1188
+ asNullableString3(data["counterparty_postalcode"]),
1189
+ asNullableString3(data["counterparty_email"]),
1190
+ asNullableString3(data["buyer_reference"]),
1191
+ data["document_type"] === void 0 || data["document_type"] === null ? ComplianceDocumentType.INVOICE : toComplianceDocumentType(data["document_type"]),
1192
+ asNullableString3(data["amended_number"]),
1193
+ asNullableString3(data["amended_date"]),
1194
+ asNullableString3(data["provider_state"]),
1195
+ toInvoiceLines(data["provider_payload"])
1063
1196
  );
1064
1197
  }
1065
1198
  };
@@ -1118,10 +1251,134 @@ var ImportResult = class _ImportResult {
1118
1251
  }
1119
1252
  };
1120
1253
 
1121
- // src/dto/RevenueStatistics.ts
1254
+ // src/enums/PeppolLookupStatus.ts
1255
+ var PeppolLookupStatus = {
1256
+ REACHABLE: "reachable",
1257
+ NOT_REACHABLE: "not_reachable",
1258
+ PENDING: "pending",
1259
+ UNKNOWN: "unknown"
1260
+ };
1261
+ var VALUES7 = Object.values(PeppolLookupStatus);
1262
+ function toPeppolLookupStatus(value) {
1263
+ if (typeof value === "string") {
1264
+ const normalized = value.trim().toLowerCase();
1265
+ if (VALUES7.includes(normalized)) {
1266
+ return normalized;
1267
+ }
1268
+ }
1269
+ return PeppolLookupStatus.UNKNOWN;
1270
+ }
1271
+ var DESCRIPTIONS7 = {
1272
+ [PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
1273
+ [PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
1274
+ [PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
1275
+ [PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
1276
+ };
1277
+ function describePeppolLookupStatus(status) {
1278
+ return DESCRIPTIONS7[status];
1279
+ }
1280
+
1281
+ // src/dto/PeppolLookupResult.ts
1122
1282
  function asString4(value, fallback = "") {
1123
1283
  return typeof value === "string" ? value : fallback;
1124
1284
  }
1285
+ function asNullableString4(value) {
1286
+ return typeof value === "string" ? value : null;
1287
+ }
1288
+ var PeppolLookupResult = class _PeppolLookupResult {
1289
+ constructor(status, reachable, country, scheme, id, documentTypes, transportTypeCode, checkedAt) {
1290
+ this.status = status;
1291
+ this.reachable = reachable;
1292
+ this.country = country;
1293
+ this.scheme = scheme;
1294
+ this.id = id;
1295
+ this.documentTypes = documentTypes;
1296
+ this.transportTypeCode = transportTypeCode;
1297
+ this.checkedAt = checkedAt;
1298
+ }
1299
+ status;
1300
+ reachable;
1301
+ country;
1302
+ scheme;
1303
+ id;
1304
+ documentTypes;
1305
+ transportTypeCode;
1306
+ checkedAt;
1307
+ /** True while the directory is still resolving — ask again in a few seconds. */
1308
+ get isPending() {
1309
+ return this.status === PeppolLookupStatus.PENDING;
1310
+ }
1311
+ static fromArray(data) {
1312
+ const documentTypes = Array.isArray(data["document_types"]) ? data["document_types"].filter((t) => typeof t === "string") : [];
1313
+ return new _PeppolLookupResult(
1314
+ toPeppolLookupStatus(data["status"]),
1315
+ typeof data["reachable"] === "boolean" ? data["reachable"] : null,
1316
+ asString4(data["country"]),
1317
+ asString4(data["scheme"]),
1318
+ asString4(data["id"]),
1319
+ documentTypes,
1320
+ asNullableString4(data["transport_type_code"]),
1321
+ asNullableString4(data["checked_at"])
1322
+ );
1323
+ }
1324
+ };
1325
+
1326
+ // src/dto/RegistryCompany.ts
1327
+ function asString5(value, fallback = "") {
1328
+ return typeof value === "string" ? value : fallback;
1329
+ }
1330
+ function asNullableString5(value) {
1331
+ return typeof value === "string" ? value : null;
1332
+ }
1333
+ var RegistryCompany = class _RegistryCompany {
1334
+ constructor(orgNumber, companyName, organisationForm, vatRegistered, vatNumber, enterpriseRegister, bankrupt, underLiquidation, address, postalcode, city, country) {
1335
+ this.orgNumber = orgNumber;
1336
+ this.companyName = companyName;
1337
+ this.organisationForm = organisationForm;
1338
+ this.vatRegistered = vatRegistered;
1339
+ this.vatNumber = vatNumber;
1340
+ this.enterpriseRegister = enterpriseRegister;
1341
+ this.bankrupt = bankrupt;
1342
+ this.underLiquidation = underLiquidation;
1343
+ this.address = address;
1344
+ this.postalcode = postalcode;
1345
+ this.city = city;
1346
+ this.country = country;
1347
+ }
1348
+ orgNumber;
1349
+ companyName;
1350
+ organisationForm;
1351
+ vatRegistered;
1352
+ vatNumber;
1353
+ enterpriseRegister;
1354
+ bankrupt;
1355
+ underLiquidation;
1356
+ address;
1357
+ postalcode;
1358
+ city;
1359
+ country;
1360
+ static fromArray(data) {
1361
+ return new _RegistryCompany(
1362
+ asString5(data["org_number"]),
1363
+ asString5(data["company_name"]),
1364
+ asNullableString5(data["organisation_form"]),
1365
+ data["vat_registered"] === true,
1366
+ asNullableString5(data["vat_number"]),
1367
+ data["enterprise_register"] === true,
1368
+ data["bankrupt"] === true,
1369
+ data["under_liquidation"] === true,
1370
+ asNullableString5(data["address"]),
1371
+ asNullableString5(data["postalcode"]),
1372
+ asNullableString5(data["city"]),
1373
+ asString5(data["country"])
1374
+ );
1375
+ }
1376
+ };
1377
+
1378
+ // src/dto/RevenueStatistics.ts
1379
+ function asString6(value, fallback = "") {
1380
+ return typeof value === "string" ? value : fallback;
1381
+ }
1125
1382
  function asNumber4(value) {
1126
1383
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
1127
1384
  }
@@ -1171,35 +1428,35 @@ var RevenueStatistics = class _RevenueStatistics {
1171
1428
  typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
1172
1429
  );
1173
1430
  const timeSeries = asRecords(data["time_series"]).map((row) => ({
1174
- bucket: asString4(row["bucket"]),
1431
+ bucket: asString6(row["bucket"]),
1175
1432
  ...toTotals(row)
1176
1433
  }));
1177
1434
  const byType = asRecords(data["by_type"]).map((row) => ({
1178
- type: asString4(row["type"]),
1179
- typeLabel: asString4(row["type_label"]),
1435
+ type: asString6(row["type"]),
1436
+ typeLabel: asString6(row["type_label"]),
1180
1437
  count: asNumber4(row["count"]),
1181
1438
  total: asMoney3(row["total"])
1182
1439
  }));
1183
1440
  const byCountry = asRecords(data["by_country"]).map((row) => ({
1184
- country: asString4(row["country"], "unknown"),
1441
+ country: asString6(row["country"], "unknown"),
1185
1442
  count: asNumber4(row["count"]),
1186
1443
  total: asMoney3(row["total"])
1187
1444
  }));
1188
1445
  const byState = asRecords(data["by_state"]).map((row) => ({
1189
- state: asString4(row["state"]),
1190
- stateLabel: asString4(row["state_label"]),
1446
+ state: asString6(row["state"]),
1447
+ stateLabel: asString6(row["state_label"]),
1191
1448
  count: asNumber4(row["count"]),
1192
1449
  total: asMoney3(row["total"])
1193
1450
  }));
1194
1451
  const byCurrency = asRecords(data["by_currency"]).map((row) => ({
1195
- currency: asString4(row["currency"]),
1452
+ currency: asString6(row["currency"]),
1196
1453
  ...toTotals(row)
1197
1454
  }));
1198
1455
  return new _RevenueStatistics(
1199
- asString4(range["date_from"]) || void 0,
1200
- asString4(range["date_to"]) || void 0,
1201
- asString4(range["interval"]) || void 0,
1202
- asString4(data["primary_currency"], "EUR"),
1456
+ asString6(range["date_from"]) || void 0,
1457
+ asString6(range["date_to"]) || void 0,
1458
+ asString6(range["interval"]) || void 0,
1459
+ asString6(data["primary_currency"], "EUR"),
1203
1460
  data["is_multi_currency"] === true,
1204
1461
  totals,
1205
1462
  timeSeries,
@@ -1212,10 +1469,10 @@ var RevenueStatistics = class _RevenueStatistics {
1212
1469
  };
1213
1470
 
1214
1471
  // src/dto/SireneLookupResult.ts
1215
- function asString5(value, fallback = "") {
1472
+ function asString7(value, fallback = "") {
1216
1473
  return typeof value === "string" ? value : fallback;
1217
1474
  }
1218
- function asNullableString4(value) {
1475
+ function asNullableString6(value) {
1219
1476
  return typeof value === "string" ? value : null;
1220
1477
  }
1221
1478
  var SireneLookupResult = class _SireneLookupResult {
@@ -1241,21 +1498,21 @@ var SireneLookupResult = class _SireneLookupResult {
1241
1498
  postalcode;
1242
1499
  static fromArray(data) {
1243
1500
  return new _SireneLookupResult(
1244
- asString5(data["company_name"]),
1245
- asString5(data["siren"]),
1246
- asNullableString4(data["siret"]),
1247
- asNullableString4(data["naf_code"]),
1248
- asNullableString4(data["naf_full"]),
1249
- asNullableString4(data["enterprise_size"]),
1250
- asNullableString4(data["address"]),
1251
- asNullableString4(data["city"]),
1252
- asNullableString4(data["postalcode"])
1501
+ asString7(data["company_name"]),
1502
+ asString7(data["siren"]),
1503
+ asNullableString6(data["siret"]),
1504
+ asNullableString6(data["naf_code"]),
1505
+ asNullableString6(data["naf_full"]),
1506
+ asNullableString6(data["enterprise_size"]),
1507
+ asNullableString6(data["address"]),
1508
+ asNullableString6(data["city"]),
1509
+ asNullableString6(data["postalcode"])
1253
1510
  );
1254
1511
  }
1255
1512
  };
1256
1513
 
1257
1514
  // src/dto/VatRates.ts
1258
- function asString6(value, fallback = "") {
1515
+ function asString8(value, fallback = "") {
1259
1516
  return typeof value === "string" ? value : fallback;
1260
1517
  }
1261
1518
  function asNumber5(value) {
@@ -1273,12 +1530,12 @@ var VatRates = class _VatRates {
1273
1530
  static fromArray(data) {
1274
1531
  const rates = (Array.isArray(data["rates"]) ? data["rates"] : []).filter((row) => typeof row === "object" && row !== null).map((row) => ({
1275
1532
  percent: asNumber5(row["percent"]),
1276
- category: asString6(row["category"]),
1277
- labelKey: asString6(row["label_key"]),
1278
- taxName: asString6(row["tax_name"]),
1533
+ category: asString8(row["category"]),
1534
+ labelKey: asString8(row["label_key"]),
1535
+ taxName: asString8(row["tax_name"]),
1279
1536
  requiresVatex: row["requires_vatex"] === true
1280
1537
  }));
1281
- return new _VatRates(asString6(data["country"]), asString6(data["currency"], "EUR"), rates);
1538
+ return new _VatRates(asString8(data["country"]), asString8(data["currency"], "EUR"), rates);
1282
1539
  }
1283
1540
  };
1284
1541
 
@@ -1297,10 +1554,48 @@ var ValidationException = class extends TaxoraException {
1297
1554
  }
1298
1555
  };
1299
1556
 
1557
+ // src/support/NorwegianOrgNumber.ts
1558
+ var WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2];
1559
+ var NorwegianOrgNumber = {
1560
+ /** Peppol identifier scheme for Norwegian organisation numbers. */
1561
+ PEPPOL_SCHEME: "0192",
1562
+ /**
1563
+ * Strips whitespace, dots, dashes, a "0192:" Peppol scheme prefix, the "NO"
1564
+ * prefix and the "MVA" suffix: "NO 923 609 016 MVA", "923609016MVA" and
1565
+ * "923 609 016" all become "923609016". Does not validate.
1566
+ */
1567
+ normalize(value) {
1568
+ let v = (value ?? "").trim().toUpperCase().replace(/[\s.-]/g, "");
1569
+ if (v.startsWith("0192:")) v = v.slice(5);
1570
+ if (v.startsWith("NO")) v = v.slice(2);
1571
+ if (v.endsWith("MVA")) v = v.slice(0, -3);
1572
+ return v;
1573
+ },
1574
+ /** True when the (normalized) value is 9 digits with a valid mod-11 check digit. */
1575
+ isValid(value) {
1576
+ const digits = NorwegianOrgNumber.normalize(value);
1577
+ if (!/^\d{9}$/.test(digits)) return false;
1578
+ let sum = 0;
1579
+ for (let i = 0; i < WEIGHTS.length; i++) {
1580
+ sum += Number(digits[i]) * WEIGHTS[i];
1581
+ }
1582
+ let check = 11 - sum % 11;
1583
+ if (check === 11) check = 0;
1584
+ return check !== 10 && check === Number(digits[8]);
1585
+ },
1586
+ /** "NO923609016MVA" — the Norwegian VAT number of an MVA-registered entity. Does not validate. */
1587
+ toVatNumber(value) {
1588
+ return `NO${NorwegianOrgNumber.normalize(value)}MVA`;
1589
+ }
1590
+ };
1591
+
1300
1592
  // src/endpoints/EReportingEndpoint.ts
1301
1593
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
1302
1594
  var NAF_CODE_REGEX = /^\d{2}$/;
1595
+ var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
1596
+ var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
1303
1597
  var INTERVALS = ["day", "week", "month"];
1598
+ var SERVICES = ["e_reporting", "e_invoicing"];
1304
1599
  var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
1305
1600
  var TYPE_OPERATIONS = ["services", "goods", "mixed"];
1306
1601
  var TRANSACTION_TYPES = [
@@ -1328,14 +1623,23 @@ var EReportingEndpoint = class {
1328
1623
  // ---------------------------------------------------------------------
1329
1624
  /**
1330
1625
  * Create a compliance enrollment and provision the provider account. By
1331
- * default the DGFiP regime is activated right away; pass `autoActivate: false`
1626
+ * default the country regime is activated right away (France: DGFiP tax
1627
+ * report setting; Norway: Peppol registration); pass `autoActivate: false`
1332
1628
  * to only create the account.
1333
1629
  *
1334
- * On provider failure the API responds 502 (mapped to an HttpException) and
1335
- * the enrollment is persisted in `error_account`/`error_activation` state so
1630
+ * The input shape depends on `country`: omitted/"FR" takes the France input
1631
+ * (unchanged, backwards compatible), "NO" the Norway input — see also the
1632
+ * createNorwayEnrollment() shortcut.
1633
+ *
1634
+ * Errors: 403 (HttpException, body code `service_not_active`) when the company
1635
+ * lacks the requested service; on provider failure 502 (HttpException) — the
1636
+ * enrollment is then persisted in `error_account`/`error_activation` state so
1336
1637
  * provisioning can be retried.
1337
1638
  */
1338
1639
  async createEnrollment(input) {
1640
+ if (this.isNorwayEnrollmentInput(input)) {
1641
+ return this.createEnrollmentFromBody(this.buildNorwayEnrollmentBody(input));
1642
+ }
1339
1643
  this.validateEnrollmentInput(input);
1340
1644
  const body = {
1341
1645
  email: input.email,
@@ -1345,6 +1649,7 @@ var EReportingEndpoint = class {
1345
1649
  reporting_start_date: this.formatDate(input.reportingStartDate)
1346
1650
  };
1347
1651
  if (input.country !== void 0) body["country"] = input.country;
1652
+ if (input.service !== void 0) body["service"] = input.service;
1348
1653
  if (input.regime !== void 0) body["regime"] = input.regime;
1349
1654
  if (input.siret !== void 0) body["siret"] = input.siret;
1350
1655
  if (input.siren !== void 0) body["siren"] = input.siren;
@@ -1355,9 +1660,15 @@ var EReportingEndpoint = class {
1355
1660
  if (input.postalcode !== void 0) body["postalcode"] = input.postalcode;
1356
1661
  if (input.province !== void 0) body["province"] = input.province;
1357
1662
  if (input.autoActivate !== void 0) body["auto_activate"] = input.autoActivate;
1358
- const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/enrollments`, body);
1359
- const data = await this.parseJsonResponse(response);
1360
- return ComplianceEnrollment.fromArray(data);
1663
+ return this.createEnrollmentFromBody(body);
1664
+ }
1665
+ /**
1666
+ * Enroll the company for Norway e-invoicing (EHF / Peppol BIS 3.0) — shortcut
1667
+ * for createEnrollment({ country: 'NO', ... }). The organisation number is
1668
+ * checked (mod-11) and normalized to 9 digits before sending.
1669
+ */
1670
+ async createNorwayEnrollment(input) {
1671
+ return this.createEnrollment({ ...input, country: "NO" });
1361
1672
  }
1362
1673
  /** Paginated list of the company's enrollments (newest first). `perPage` is capped at 100 server-side. */
1363
1674
  async listEnrollments(page = 1, perPage = 25) {
@@ -1391,6 +1702,72 @@ var EReportingEndpoint = class {
1391
1702
  const data = await this.parseJsonResponse(response);
1392
1703
  return SireneLookupResult.fromArray(data);
1393
1704
  }
1705
+ /**
1706
+ * Look up companies in a national company register — currently Norway
1707
+ * (Brønnøysund Enhetsregisteret). `q` is an organisation number, a "NO…MVA"
1708
+ * VAT number or a company name; an exact number match yields one result.
1709
+ * Ideal for prefilling createNorwayEnrollment() and the buyer fields of an
1710
+ * e-invoice. For France use sireneLookup().
1711
+ *
1712
+ * Errors: 404 (HttpException) when nothing is found, 422 (ValidationException)
1713
+ * on invalid input / unsupported country, 429 (HttpException) when the
1714
+ * per-user rate limit (30/min) is exceeded.
1715
+ */
1716
+ async registryLookup(country, q) {
1717
+ const normalizedCountry = this.normalizeLookupCountry(country);
1718
+ const query = q.trim();
1719
+ if (query === "") {
1720
+ throw new TaxoraException("q must not be empty.");
1721
+ }
1722
+ if (normalizedCountry === "NO") {
1723
+ const digits = NorwegianOrgNumber.normalize(query);
1724
+ if (/^\d{9}$/.test(digits) && !NorwegianOrgNumber.isValid(digits)) {
1725
+ throw new TaxoraException(`Invalid Norwegian organisation number "${query}" (check digit).`);
1726
+ }
1727
+ }
1728
+ const params = new URLSearchParams();
1729
+ params.set("country", normalizedCountry);
1730
+ params.set("q", query);
1731
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/registry-lookup?${params.toString()}`);
1732
+ const data = await this.parseJsonResponse(response);
1733
+ const results = Array.isArray(data["results"]) ? data["results"] : [];
1734
+ return results.filter((row) => typeof row === "object" && row !== null && !Array.isArray(row)).map((row) => RegistryCompany.fromArray(row));
1735
+ }
1736
+ /**
1737
+ * Check whether a participant (typically the buyer) can receive e-invoices
1738
+ * over Peppol. For Norway pass the organisation number (scheme defaults to
1739
+ * "0192"): `reachable === true` means the buyer is registered in ELMA and must
1740
+ * receive an EHF e-invoice. The directory resolves asynchronously —
1741
+ * `status === 'pending'` (`reachable === null`) means ask again in a few
1742
+ * seconds. Free of charge; definite answers are cached for 24h server-side.
1743
+ *
1744
+ * A Norwegian organisation number is checked (mod-11) and normalized before
1745
+ * sending. Errors: 422 (ValidationException) on invalid input, 502
1746
+ * (HttpException) on provider failure.
1747
+ */
1748
+ async peppolLookup(country, id, scheme) {
1749
+ const normalizedCountry = this.normalizeLookupCountry(country);
1750
+ let participantId = id.trim();
1751
+ if (participantId === "") {
1752
+ throw new TaxoraException("id must not be empty.");
1753
+ }
1754
+ if (scheme !== void 0 && !PEPPOL_SCHEME_REGEX.test(scheme)) {
1755
+ throw new TaxoraException('scheme must be a 4-digit Peppol identifier scheme, e.g. "0192".');
1756
+ }
1757
+ if (normalizedCountry === "NO" && (scheme === void 0 || scheme === NorwegianOrgNumber.PEPPOL_SCHEME)) {
1758
+ if (!NorwegianOrgNumber.isValid(participantId)) {
1759
+ throw new TaxoraException(`Invalid Norwegian organisation number "${participantId}".`);
1760
+ }
1761
+ participantId = NorwegianOrgNumber.normalize(participantId);
1762
+ }
1763
+ const params = new URLSearchParams();
1764
+ params.set("country", normalizedCountry);
1765
+ params.set("id", participantId);
1766
+ if (scheme !== void 0) params.set("scheme", scheme);
1767
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/peppol-lookup?${params.toString()}`);
1768
+ const data = await this.parseJsonResponse(response);
1769
+ return PeppolLookupResult.fromArray(data);
1770
+ }
1394
1771
  // ---------------------------------------------------------------------
1395
1772
  // Transactions
1396
1773
  // ---------------------------------------------------------------------
@@ -1414,6 +1791,7 @@ var EReportingEndpoint = class {
1414
1791
  this.validateInvoiceNumber(input.invoiceNumber);
1415
1792
  this.validateInvoiceLines(input.invoiceLines);
1416
1793
  this.validateOptionalCodes(input);
1794
+ this.validateBuyerFields(input);
1417
1795
  const body = {
1418
1796
  compliance_enrollment_id: input.complianceEnrollmentId,
1419
1797
  transaction_type: input.transactionType,
@@ -1429,13 +1807,23 @@ var EReportingEndpoint = class {
1429
1807
  const data = await this.parseJsonResponse(response);
1430
1808
  return ComplianceTransaction.fromArray(data);
1431
1809
  }
1432
- /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type. */
1810
+ /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country. */
1433
1811
  async listTransactions(filters = {}) {
1812
+ if (filters.complianceEnrollmentId !== void 0) {
1813
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
1814
+ }
1815
+ if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
1816
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
1817
+ }
1434
1818
  const params = new URLSearchParams();
1435
1819
  if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
1436
1820
  if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
1437
1821
  if (filters.state !== void 0) params.set("state", filters.state);
1438
1822
  if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
1823
+ if (filters.complianceEnrollmentId !== void 0) {
1824
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
1825
+ }
1826
+ if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
1439
1827
  if (filters.page !== void 0) params.set("page", String(filters.page));
1440
1828
  if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
1441
1829
  const queryString = params.toString();
@@ -1457,6 +1845,7 @@ var EReportingEndpoint = class {
1457
1845
  if (input.invoiceNumber !== void 0) this.validateInvoiceNumber(input.invoiceNumber);
1458
1846
  if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
1459
1847
  this.validateOptionalCodes(input);
1848
+ this.validateBuyerFields(input);
1460
1849
  const body = {};
1461
1850
  if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
1462
1851
  if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
@@ -1579,6 +1968,14 @@ var EReportingEndpoint = class {
1579
1968
  * returned as HTTP 400 (surfaced as HttpException), not 422.
1580
1969
  */
1581
1970
  async requestEReportingAccess(input = {}) {
1971
+ if (input.service !== void 0) this.validateService(input.service);
1972
+ if (input.countries !== void 0) {
1973
+ input.countries.forEach((country, index) => {
1974
+ if (!COUNTRY_REGEX.test(country)) {
1975
+ throw new TaxoraException(`countries[${index}] must be a 2-character ISO 3166-1 alpha-2 code.`);
1976
+ }
1977
+ });
1978
+ }
1582
1979
  const body = {};
1583
1980
  if (input.name !== void 0) body["name"] = input.name;
1584
1981
  if (input.email !== void 0) body["email"] = input.email;
@@ -1586,6 +1983,8 @@ var EReportingEndpoint = class {
1586
1983
  if (input.phone !== void 0) body["phone"] = input.phone;
1587
1984
  if (input.message !== void 0) body["message"] = input.message;
1588
1985
  if (input.language !== void 0) body["language"] = input.language;
1986
+ if (input.service !== void 0) body["service"] = input.service;
1987
+ if (input.countries !== void 0) body["countries"] = input.countries.map((c) => c.toUpperCase());
1589
1988
  const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/e-reporting-request`, body);
1590
1989
  await this.parseJsonResponse(response);
1591
1990
  }
@@ -1621,6 +2020,73 @@ var EReportingEndpoint = class {
1621
2020
  if (input.country !== void 0 && input.country.length !== 2) {
1622
2021
  throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
1623
2022
  }
2023
+ if (input.service !== void 0) this.validateService(input.service);
2024
+ }
2025
+ isNorwayEnrollmentInput(input) {
2026
+ return typeof input.country === "string" && input.country.trim().toUpperCase() === "NO";
2027
+ }
2028
+ /** Validates the Norway input and builds its wire body (org number normalized to 9 digits). */
2029
+ buildNorwayEnrollmentBody(input) {
2030
+ if (typeof input.orgNumber !== "string" || !NorwegianOrgNumber.isValid(input.orgNumber)) {
2031
+ throw new TaxoraException(
2032
+ "orgNumber must be a valid 9-digit Norwegian organisation number (mod-11 check digit)."
2033
+ );
2034
+ }
2035
+ for (const field of ["companyName", "address", "city", "postalcode"]) {
2036
+ if (typeof input[field] !== "string" || input[field].trim() === "") {
2037
+ throw new TaxoraException(`${field} is required for a Norway enrollment.`);
2038
+ }
2039
+ }
2040
+ this.validateEmail(input.email, "email");
2041
+ if (input.service !== void 0) this.validateService(input.service);
2042
+ if (input.legalCountry !== void 0 && !COUNTRY_REGEX.test(input.legalCountry)) {
2043
+ throw new TaxoraException("legalCountry must be a 2-character ISO 3166-1 alpha-2 code.");
2044
+ }
2045
+ const body = {
2046
+ country: "NO",
2047
+ org_number: NorwegianOrgNumber.normalize(input.orgNumber),
2048
+ company_name: input.companyName,
2049
+ address: input.address,
2050
+ city: input.city,
2051
+ postalcode: input.postalcode,
2052
+ email: input.email
2053
+ };
2054
+ if (input.service !== void 0) body["service"] = input.service;
2055
+ if (input.regime !== void 0) body["regime"] = input.regime;
2056
+ if (input.vatNumber !== void 0) body["vat_number"] = input.vatNumber;
2057
+ if (input.province !== void 0) body["province"] = input.province;
2058
+ if (input.vatRegistered !== void 0) body["vat_registered"] = input.vatRegistered;
2059
+ if (input.enterpriseRegister !== void 0) body["enterprise_register"] = input.enterpriseRegister;
2060
+ if (input.reception !== void 0) body["reception"] = input.reception;
2061
+ if (input.legalCountry !== void 0) body["legal_country"] = input.legalCountry.toUpperCase();
2062
+ if (input.reportingStartDate !== void 0) {
2063
+ body["reporting_start_date"] = this.formatDate(input.reportingStartDate);
2064
+ }
2065
+ if (input.autoActivate !== void 0) body["auto_activate"] = input.autoActivate;
2066
+ return body;
2067
+ }
2068
+ async createEnrollmentFromBody(body) {
2069
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/enrollments`, body);
2070
+ const data = await this.parseJsonResponse(response);
2071
+ return ComplianceEnrollment.fromArray(data);
2072
+ }
2073
+ validateService(service) {
2074
+ if (!SERVICES.includes(service)) {
2075
+ throw new TaxoraException(`Invalid service "${String(service)}". Expected one of: ${SERVICES.join(", ")}.`);
2076
+ }
2077
+ }
2078
+ validateEmail(email, name) {
2079
+ if (typeof email !== "string" || email.trim() === "" || !email.includes("@")) {
2080
+ throw new TaxoraException(`${name} must be a valid email address.`);
2081
+ }
2082
+ }
2083
+ /** Lookup routes take a 2-letter country; the API expects it upper-case. */
2084
+ normalizeLookupCountry(country) {
2085
+ const normalized = country.trim().toUpperCase();
2086
+ if (!COUNTRY_REGEX.test(normalized)) {
2087
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2088
+ }
2089
+ return normalized;
1624
2090
  }
1625
2091
  validateId(id, name) {
1626
2092
  if (!Number.isInteger(id) || id <= 0) {
@@ -1660,6 +2126,33 @@ var EReportingEndpoint = class {
1660
2126
  throw new TaxoraException("counterpartyCountry must be a 2-character ISO 3166-1 alpha-2 code.");
1661
2127
  }
1662
2128
  }
2129
+ /**
2130
+ * Format checks for the buyer/document fields. Country rules (e.g. the Norway
2131
+ * org number being required) depend on the enrollment and stay server-side;
2132
+ * a register id is only check-digit validated when the buyer is Norwegian.
2133
+ */
2134
+ validateBuyerFields(input) {
2135
+ if (input.counterpartyRegisterId !== void 0) {
2136
+ if (input.counterpartyRegisterId.length > 30) {
2137
+ throw new TaxoraException("counterpartyRegisterId must not exceed 30 characters.");
2138
+ }
2139
+ if (input.counterpartyCountry?.toUpperCase() === "NO" && !NorwegianOrgNumber.isValid(input.counterpartyRegisterId)) {
2140
+ throw new TaxoraException(
2141
+ "counterpartyRegisterId must be a valid 9-digit Norwegian organisation number (mod-11 check digit)."
2142
+ );
2143
+ }
2144
+ }
2145
+ if (input.counterpartyEmail !== void 0) this.validateEmail(input.counterpartyEmail, "counterpartyEmail");
2146
+ if (input.buyerReference !== void 0 && input.buyerReference.length > 100) {
2147
+ throw new TaxoraException("buyerReference must not exceed 100 characters.");
2148
+ }
2149
+ if (input.amendedNumber !== void 0 && input.amendedNumber.length > 50) {
2150
+ throw new TaxoraException("amendedNumber must not exceed 50 characters.");
2151
+ }
2152
+ if (input.extraInfo !== void 0 && input.extraInfo.length > 1e3) {
2153
+ throw new TaxoraException("extraInfo must not exceed 1000 characters.");
2154
+ }
2155
+ }
1663
2156
  // ---------------------------------------------------------------------
1664
2157
  // Body building (camelCase input → snake_case wire format)
1665
2158
  // ---------------------------------------------------------------------
@@ -1696,6 +2189,16 @@ var EReportingEndpoint = class {
1696
2189
  if (input.remittanceInformation !== void 0) body["remittance_information"] = input.remittanceInformation;
1697
2190
  if (input.paymentMethodText !== void 0) body["payment_method_text"] = input.paymentMethodText;
1698
2191
  if (input.paymentTerms !== void 0) body["payment_terms"] = input.paymentTerms;
2192
+ if (input.counterpartyRegisterId !== void 0) body["counterparty_register_id"] = input.counterpartyRegisterId;
2193
+ if (input.counterpartyAddress !== void 0) body["counterparty_address"] = input.counterpartyAddress;
2194
+ if (input.counterpartyCity !== void 0) body["counterparty_city"] = input.counterpartyCity;
2195
+ if (input.counterpartyPostalcode !== void 0) body["counterparty_postalcode"] = input.counterpartyPostalcode;
2196
+ if (input.counterpartyEmail !== void 0) body["counterparty_email"] = input.counterpartyEmail;
2197
+ if (input.buyerReference !== void 0) body["buyer_reference"] = input.buyerReference;
2198
+ if (input.isCreditNote !== void 0) body["is_credit_note"] = input.isCreditNote;
2199
+ if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
2200
+ if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
2201
+ if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
1699
2202
  }
1700
2203
  // ---------------------------------------------------------------------
1701
2204
  // HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
@@ -1818,22 +2321,59 @@ var SmartEnrichmentMode = {
1818
2321
  DEFAULT: "default",
1819
2322
  COMPLEX: "complex"
1820
2323
  };
1821
- var VALUES5 = Object.values(SmartEnrichmentMode);
2324
+ var VALUES8 = Object.values(SmartEnrichmentMode);
1822
2325
  function toSmartEnrichmentMode(value) {
1823
2326
  if (typeof value === "string") {
1824
2327
  const normalized = value.trim().toLowerCase();
1825
- if (VALUES5.includes(normalized)) {
2328
+ if (VALUES8.includes(normalized)) {
1826
2329
  return normalized;
1827
2330
  }
1828
2331
  }
1829
2332
  return void 0;
1830
2333
  }
1831
- var DESCRIPTIONS5 = {
2334
+ var DESCRIPTIONS8 = {
1832
2335
  [SmartEnrichmentMode.DEFAULT]: "One provider searches; a second is consulted only if it finds nothing.",
1833
2336
  [SmartEnrichmentMode.COMPLEX]: "Two providers search independently and are cross-checked against the tax authority. Costs more."
1834
2337
  };
1835
2338
  function describeSmartEnrichmentMode(mode) {
1836
- return DESCRIPTIONS5[mode];
2339
+ return DESCRIPTIONS8[mode];
2340
+ }
2341
+
2342
+ // src/enums/SmartEnrichmentRejectionReason.ts
2343
+ var SmartEnrichmentRejectionReason = {
2344
+ /** The tax authority reports the number as not (or no longer) valid. */
2345
+ VAT_INVALID: "vat_invalid",
2346
+ /** The number is valid, but registered to a different company. */
2347
+ NAME_MISMATCH: "name_mismatch",
2348
+ /** The number could not be tied to the requested company. */
2349
+ UNBOUND: "unbound",
2350
+ /** The number could not be checked against the tax authority. */
2351
+ UNVERIFIED: "unverified",
2352
+ /** Rejected for another reason. */
2353
+ REJECTED: "rejected",
2354
+ /** A reason this SDK version does not know yet. */
2355
+ UNKNOWN: "unknown"
2356
+ };
2357
+ var VALUES9 = Object.values(SmartEnrichmentRejectionReason);
2358
+ function toSmartEnrichmentRejectionReason(value) {
2359
+ if (typeof value === "string") {
2360
+ const normalized = value.trim().toLowerCase();
2361
+ if (VALUES9.includes(normalized)) {
2362
+ return normalized;
2363
+ }
2364
+ }
2365
+ return SmartEnrichmentRejectionReason.UNKNOWN;
2366
+ }
2367
+ var DESCRIPTIONS9 = {
2368
+ [SmartEnrichmentRejectionReason.VAT_INVALID]: "The tax authority reports the VAT number as invalid.",
2369
+ [SmartEnrichmentRejectionReason.NAME_MISMATCH]: "The VAT number is registered to a different company.",
2370
+ [SmartEnrichmentRejectionReason.UNBOUND]: "The VAT number could not be tied to the requested company.",
2371
+ [SmartEnrichmentRejectionReason.UNVERIFIED]: "The VAT number could not be verified with the tax authority.",
2372
+ [SmartEnrichmentRejectionReason.REJECTED]: "The VAT number was not confirmed.",
2373
+ [SmartEnrichmentRejectionReason.UNKNOWN]: "Unknown rejection reason."
2374
+ };
2375
+ function describeSmartEnrichmentRejectionReason(reason) {
2376
+ return DESCRIPTIONS9[reason];
1837
2377
  }
1838
2378
 
1839
2379
  // src/enums/SmartEnrichmentStatus.ts
@@ -1847,17 +2387,17 @@ var SmartEnrichmentStatus = {
1847
2387
  FAILED: "failed",
1848
2388
  UNKNOWN: "unknown"
1849
2389
  };
1850
- var VALUES6 = Object.values(SmartEnrichmentStatus);
2390
+ var VALUES10 = Object.values(SmartEnrichmentStatus);
1851
2391
  function toSmartEnrichmentStatus(value) {
1852
2392
  if (typeof value === "string") {
1853
2393
  const normalized = value.trim().toLowerCase();
1854
- if (VALUES6.includes(normalized)) {
2394
+ if (VALUES10.includes(normalized)) {
1855
2395
  return normalized;
1856
2396
  }
1857
2397
  }
1858
2398
  return SmartEnrichmentStatus.UNKNOWN;
1859
2399
  }
1860
- var DESCRIPTIONS6 = {
2400
+ var DESCRIPTIONS10 = {
1861
2401
  [SmartEnrichmentStatus.FOUND]: "A VAT number was resolved and confirmed.",
1862
2402
  [SmartEnrichmentStatus.NO_VAT_EXISTS]: "The company was identified but has no VAT number.",
1863
2403
  [SmartEnrichmentStatus.NOT_FOUND]: "No sufficiently-confident match could be resolved.",
@@ -1868,12 +2408,30 @@ var DESCRIPTIONS6 = {
1868
2408
  [SmartEnrichmentStatus.UNKNOWN]: "Unknown status."
1869
2409
  };
1870
2410
  function describeSmartEnrichmentStatus(status) {
1871
- return DESCRIPTIONS6[status];
2411
+ return DESCRIPTIONS10[status];
1872
2412
  }
1873
2413
 
1874
2414
  // src/dto/SmartEnrichmentResource.ts
2415
+ function toRejectedCandidates(value) {
2416
+ if (!Array.isArray(value)) {
2417
+ return [];
2418
+ }
2419
+ const candidates = [];
2420
+ for (const entry of value) {
2421
+ if (typeof entry !== "object" || entry === null) {
2422
+ continue;
2423
+ }
2424
+ const record = entry;
2425
+ const vatNumber = typeof record["vatNumber"] === "string" ? record["vatNumber"].trim() : "";
2426
+ if (vatNumber === "") {
2427
+ continue;
2428
+ }
2429
+ candidates.push({ vatNumber, reason: toSmartEnrichmentRejectionReason(record["reason"]) });
2430
+ }
2431
+ return candidates;
2432
+ }
1875
2433
  var SmartEnrichmentResource = class _SmartEnrichmentResource {
1876
- constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0) {
2434
+ constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0, rejectedCandidates = []) {
1877
2435
  this.status = status;
1878
2436
  this.vatNumber = vatNumber;
1879
2437
  this.vatType = vatType;
@@ -1885,6 +2443,7 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
1885
2443
  this.mode = mode;
1886
2444
  this.providerVerdicts = providerVerdicts;
1887
2445
  this.addressQuality = addressQuality;
2446
+ this.rejectedCandidates = rejectedCandidates;
1888
2447
  }
1889
2448
  status;
1890
2449
  vatNumber;
@@ -1897,6 +2456,11 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
1897
2456
  mode;
1898
2457
  providerVerdicts;
1899
2458
  addressQuality;
2459
+ rejectedCandidates;
2460
+ /** True when a VAT number was found but rejected by the official check. */
2461
+ hasRejectedCandidates() {
2462
+ return this.rejectedCandidates.length > 0;
2463
+ }
1900
2464
  static fromArray(data) {
1901
2465
  const str = (key) => {
1902
2466
  const v = data[key];
@@ -1919,7 +2483,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
1919
2483
  str("source"),
1920
2484
  toSmartEnrichmentMode(data["mode"]),
1921
2485
  Array.isArray(verdicts) ? verdicts.filter((v) => typeof v === "object" && v !== null) : [],
1922
- typeof quality === "object" && quality !== null ? quality : void 0
2486
+ typeof quality === "object" && quality !== null ? quality : void 0,
2487
+ toRejectedCandidates(data["rejectedCandidates"])
1923
2488
  );
1924
2489
  }
1925
2490
  toArray() {
@@ -1934,7 +2499,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
1934
2499
  source: this.source ?? null,
1935
2500
  mode: this.mode ?? null,
1936
2501
  providerVerdicts: this.providerVerdicts,
1937
- addressQuality: this.addressQuality ?? null
2502
+ addressQuality: this.addressQuality ?? null,
2503
+ rejectedCandidates: this.rejectedCandidates.map((c) => ({ vatNumber: c.vatNumber, reason: c.reason }))
1938
2504
  };
1939
2505
  }
1940
2506
  };
@@ -2086,13 +2652,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
2086
2652
  };
2087
2653
 
2088
2654
  // src/dto/SmartEnrichmentStatistics.ts
2089
- function asString7(value, fallback = "") {
2655
+ function asString9(value, fallback = "") {
2090
2656
  return typeof value === "string" ? value : fallback;
2091
2657
  }
2092
2658
  function asNumber6(value) {
2093
2659
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
2094
2660
  }
2095
- function asRecord(value) {
2661
+ function asRecord2(value) {
2096
2662
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
2097
2663
  }
2098
2664
  function asRecords2(value) {
@@ -2115,32 +2681,32 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
2115
2681
  byOutcome;
2116
2682
  confidenceBuckets;
2117
2683
  static fromArray(data) {
2118
- const range = asRecord(data["range"]);
2119
- const totals = asRecord(data["totals"]);
2684
+ const range = asRecord2(data["range"]);
2685
+ const totals = asRecord2(data["totals"]);
2120
2686
  const timeSeries = asRecords2(data["time_series"]).map((row) => ({
2121
- bucket: asString7(row["bucket"]),
2687
+ bucket: asString9(row["bucket"]),
2122
2688
  jobs: asNumber6(row["jobs"]),
2123
2689
  items: asNumber6(row["items"]),
2124
2690
  found: asNumber6(row["found"])
2125
2691
  }));
2126
2692
  const bySource = asRecords2(data["by_source"]).map((row) => ({
2127
- source: asString7(row["source"]),
2693
+ source: asString9(row["source"]),
2128
2694
  jobs: asNumber6(row["jobs"]),
2129
2695
  found: asNumber6(row["found"])
2130
2696
  }));
2131
2697
  const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
2132
- outcome: asString7(row["outcome"]),
2698
+ outcome: asString9(row["outcome"]),
2133
2699
  count: asNumber6(row["count"])
2134
2700
  }));
2135
2701
  const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
2136
- bucket: asString7(row["bucket"]),
2702
+ bucket: asString9(row["bucket"]),
2137
2703
  count: asNumber6(row["count"])
2138
2704
  }));
2139
2705
  return new _SmartEnrichmentStatistics(
2140
2706
  {
2141
- dateFrom: asString7(range["date_from"]),
2142
- dateTo: asString7(range["date_to"]),
2143
- interval: asString7(range["interval"])
2707
+ dateFrom: asString9(range["date_from"]),
2708
+ dateTo: asString9(range["date_to"]),
2709
+ interval: asString9(range["interval"])
2144
2710
  },
2145
2711
  {
2146
2712
  jobs: asNumber6(totals["jobs"]),
@@ -2355,7 +2921,10 @@ var SmartEnrichmentEndpoint = class {
2355
2921
  async parseJsonResponse(response, acceptedStatuses = [200]) {
2356
2922
  const responseText = await response.text();
2357
2923
  if (response.status === 422) {
2358
- throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
2924
+ throw new ValidationException(
2925
+ jsonErrorMessage(responseText) ?? "Validation failed",
2926
+ this.parseValidationErrors(responseText)
2927
+ );
2359
2928
  }
2360
2929
  if (!response.ok && !acceptedStatuses.includes(response.status)) {
2361
2930
  throw new HttpException(
@@ -2389,7 +2958,10 @@ var SmartEnrichmentEndpoint = class {
2389
2958
  async parseRawJson(response) {
2390
2959
  const responseText = await response.text();
2391
2960
  if (response.status === 422) {
2392
- throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
2961
+ throw new ValidationException(
2962
+ jsonErrorMessage(responseText) ?? "Validation failed",
2963
+ this.parseValidationErrors(responseText)
2964
+ );
2393
2965
  }
2394
2966
  if (!response.ok) {
2395
2967
  throw new HttpException(
@@ -2418,7 +2990,10 @@ var SmartEnrichmentEndpoint = class {
2418
2990
  async parseCsvResponse(response) {
2419
2991
  const responseText = await response.text();
2420
2992
  if (response.status === 422) {
2421
- throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
2993
+ throw new ValidationException(
2994
+ jsonErrorMessage(responseText) ?? "Validation failed",
2995
+ this.parseValidationErrors(responseText)
2996
+ );
2422
2997
  }
2423
2998
  if (!response.ok) {
2424
2999
  throw new HttpException(
@@ -2464,14 +3039,14 @@ var VatState = {
2464
3039
  function getFailedVatStates() {
2465
3040
  return [VatState.INVALID, VatState.FRAUD];
2466
3041
  }
2467
- var DESCRIPTIONS7 = {
3042
+ var DESCRIPTIONS11 = {
2468
3043
  [VatState.VALID]: "The VAT number is valid and active.",
2469
3044
  [VatState.INVALID]: "The VAT number is invalid or not registered.",
2470
3045
  [VatState.FRAUD]: "The VAT number has been flagged as potentially fraudulent.",
2471
3046
  [VatState.UNKNOWN]: "The VAT number state could not be determined."
2472
3047
  };
2473
3048
  function describeVatState(state) {
2474
- return DESCRIPTIONS7[state];
3049
+ return DESCRIPTIONS11[state];
2475
3050
  }
2476
3051
 
2477
3052
  // src/dto/CompanyAddress.ts
@@ -3203,7 +3778,7 @@ var AuthRetryHttpClient = class {
3203
3778
  };
3204
3779
 
3205
3780
  // src/version.ts
3206
- var SDK_VERSION = "1.2.0";
3781
+ var SDK_VERSION = "1.4.0";
3207
3782
 
3208
3783
  // src/http/SdkVersionHttpClient.ts
3209
3784
  var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
@@ -3242,7 +3817,13 @@ var TaxoraClient = class {
3242
3817
  const retryClient = new AuthRetryHttpClient(versionedClient, tokenStorage, () => this.auth.refresh());
3243
3818
  this.vat = new VatEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3244
3819
  this.company = new CompanyEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3245
- this.smartEnrichment = new SmartEnrichmentEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3820
+ this.smartEnrichment = new SmartEnrichmentEndpoint(
3821
+ this.baseUrl,
3822
+ options.apiKey,
3823
+ tokenStorage,
3824
+ retryClient,
3825
+ retryPolicy
3826
+ );
3246
3827
  this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3247
3828
  }
3248
3829
  };
@@ -3264,6 +3845,125 @@ var Language = {
3264
3845
  ENGLISH: "en",
3265
3846
  GERMAN: "de"
3266
3847
  };
3848
+
3849
+ // src/webhooks/WebhookSignature.ts
3850
+ var import_node_crypto = require("crypto");
3851
+ var WEBHOOK_SIGNATURE_HEADER = "Taxora-Signature";
3852
+ var WEBHOOK_ID_HEADER = "Taxora-Webhook-Id";
3853
+ var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
3854
+ var SCHEME = "v1";
3855
+ var WebhookSignatureVerificationError = class extends TaxoraException {
3856
+ reason;
3857
+ constructor(message, reason, context = {}) {
3858
+ super(message, { ...context, reason });
3859
+ this.name = "WebhookSignatureVerificationError";
3860
+ this.reason = reason;
3861
+ }
3862
+ };
3863
+ function parseWebhookSignatureHeader(header) {
3864
+ const value = Array.isArray(header) ? header[0] : header;
3865
+ if (typeof value !== "string" || value.trim() === "") {
3866
+ throw new WebhookSignatureVerificationError(
3867
+ `Missing ${WEBHOOK_SIGNATURE_HEADER} header. Is signing enabled for this webhook endpoint?`,
3868
+ "missing_header"
3869
+ );
3870
+ }
3871
+ let timestamp = null;
3872
+ const signatures = [];
3873
+ for (const part of value.split(",")) {
3874
+ const separator = part.indexOf("=");
3875
+ if (separator === -1) continue;
3876
+ const key = part.slice(0, separator).trim();
3877
+ const val = part.slice(separator + 1).trim();
3878
+ if (key === "t" && /^\d+$/.test(val)) {
3879
+ timestamp = Number(val);
3880
+ } else if (key === SCHEME && /^[0-9a-f]{64}$/i.test(val)) {
3881
+ signatures.push(val.toLowerCase());
3882
+ }
3883
+ }
3884
+ if (timestamp === null || signatures.length === 0) {
3885
+ throw new WebhookSignatureVerificationError(
3886
+ `Malformed ${WEBHOOK_SIGNATURE_HEADER} header \u2014 expected "t=<unix>,v1=<hex>".`,
3887
+ "malformed_header"
3888
+ );
3889
+ }
3890
+ return { timestamp, signatures };
3891
+ }
3892
+ function computeWebhookSignature(rawBody, timestamp, secret) {
3893
+ return (0, import_node_crypto.createHmac)("sha256", secret).update(`${timestamp}.`).update(toBytes(rawBody)).digest("hex");
3894
+ }
3895
+ function generateWebhookSignatureHeader(rawBody, secret, timestamp = Math.floor(Date.now() / 1e3)) {
3896
+ const secrets = normalizeSecrets(secret);
3897
+ return [`t=${timestamp}`, ...secrets.map((s) => `${SCHEME}=${computeWebhookSignature(rawBody, timestamp, s)}`)].join(
3898
+ ","
3899
+ );
3900
+ }
3901
+ function verifyWebhookSignature(rawBody, header, secret, options = {}) {
3902
+ const secrets = normalizeSecrets(secret);
3903
+ const bytes = toBytes(rawBody);
3904
+ const { timestamp, signatures } = parseWebhookSignatureHeader(header);
3905
+ const tolerance = options.tolerance ?? DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
3906
+ const now = options.now ?? Math.floor(Date.now() / 1e3);
3907
+ if (tolerance > 0 && Math.abs(now - timestamp) > tolerance) {
3908
+ throw new WebhookSignatureVerificationError(
3909
+ `Timestamp outside the tolerance window (${tolerance}s). Possible replay \u2014 or check the server clock.`,
3910
+ "timestamp_outside_tolerance",
3911
+ { timestamp, now, tolerance }
3912
+ );
3913
+ }
3914
+ const received = signatures.map((s) => Buffer.from(s, "hex"));
3915
+ for (const s of secrets) {
3916
+ const expected = Buffer.from(computeWebhookSignature(bytes, timestamp, s), "hex");
3917
+ let matched = false;
3918
+ for (const candidate of received) {
3919
+ if (candidate.length === expected.length && (0, import_node_crypto.timingSafeEqual)(candidate, expected)) {
3920
+ matched = true;
3921
+ }
3922
+ }
3923
+ if (matched) return true;
3924
+ }
3925
+ throw new WebhookSignatureVerificationError(
3926
+ 'No signature matches the expected signature for this payload. Make sure you pass the raw request body (not re-serialised JSON) and the full secret including the "whsec_" prefix.',
3927
+ "no_matching_signature"
3928
+ );
3929
+ }
3930
+ function constructWebhookEvent(rawBody, header, secret, options = {}) {
3931
+ verifyWebhookSignature(rawBody, header, secret, options);
3932
+ let parsed;
3933
+ try {
3934
+ parsed = JSON.parse(Buffer.from(toBytes(rawBody)).toString("utf8"));
3935
+ } catch {
3936
+ throw new WebhookSignatureVerificationError("Webhook payload is not valid JSON.", "invalid_payload");
3937
+ }
3938
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.id !== "string" || typeof parsed.event !== "string" || typeof parsed.data !== "object") {
3939
+ throw new WebhookSignatureVerificationError(
3940
+ "Webhook payload does not have the expected { id, event, timestamp, data } shape.",
3941
+ "invalid_payload"
3942
+ );
3943
+ }
3944
+ return parsed;
3945
+ }
3946
+ function normalizeSecrets(secret) {
3947
+ const list = (typeof secret === "string" ? [secret] : [...secret]).filter(
3948
+ (s) => typeof s === "string" && s !== ""
3949
+ );
3950
+ if (list.length === 0) {
3951
+ throw new WebhookSignatureVerificationError(
3952
+ "No webhook signing secret given. Copy it from the Taxora dashboard (Developer \u2192 Webhooks).",
3953
+ "missing_secret"
3954
+ );
3955
+ }
3956
+ return list;
3957
+ }
3958
+ function toBytes(rawBody) {
3959
+ if (typeof rawBody === "string") return Buffer.from(rawBody, "utf8");
3960
+ if (rawBody instanceof Uint8Array) return rawBody;
3961
+ if (rawBody instanceof ArrayBuffer) return new Uint8Array(rawBody);
3962
+ throw new WebhookSignatureVerificationError(
3963
+ "Webhook body must be the raw request body (string, Buffer or Uint8Array). It looks like it was already parsed as JSON \u2014 use express.raw() / request.text() instead of a JSON body parser.",
3964
+ "invalid_body"
3965
+ );
3966
+ }
3267
3967
  // Annotate the CommonJS export names for ESM import in node:
3268
3968
  0 && (module.exports = {
3269
3969
  ApiVersion,
@@ -3272,9 +3972,11 @@ var Language = {
3272
3972
  AuthenticationException,
3273
3973
  CompanyAddress,
3274
3974
  CompanyEndpoint,
3975
+ ComplianceDocumentType,
3275
3976
  ComplianceEnrollment,
3276
3977
  ComplianceEnrollmentPage,
3277
3978
  ComplianceEnrollmentStatus,
3979
+ ComplianceService,
3278
3980
  ComplianceTaxReport,
3279
3981
  ComplianceTaxReportPage,
3280
3982
  ComplianceTaxReportState,
@@ -3282,6 +3984,7 @@ var Language = {
3282
3984
  ComplianceTransactionPage,
3283
3985
  ComplianceTransactionState,
3284
3986
  ComplianceTransactionType,
3987
+ DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
3285
3988
  EReportingEndpoint,
3286
3989
  Environment,
3287
3990
  FetchHttpClient,
@@ -3290,8 +3993,12 @@ var Language = {
3290
3993
  InMemoryTokenStorage,
3291
3994
  Language,
3292
3995
  LoginIdentifier,
3996
+ NorwegianOrgNumber,
3997
+ PeppolLookupResult,
3998
+ PeppolLookupStatus,
3293
3999
  ProviderDocument,
3294
4000
  ProviderDocumentLine,
4001
+ RegistryCompany,
3295
4002
  RetryPolicy,
3296
4003
  RevenueStatistics,
3297
4004
  ScoreBreakdown,
@@ -3301,6 +4008,7 @@ var Language = {
3301
4008
  SmartEnrichmentHistoryRow,
3302
4009
  SmartEnrichmentJob,
3303
4010
  SmartEnrichmentMode,
4011
+ SmartEnrichmentRejectionReason,
3304
4012
  SmartEnrichmentResource,
3305
4013
  SmartEnrichmentStatistics,
3306
4014
  SmartEnrichmentStatus,
@@ -3316,21 +4024,37 @@ var Language = {
3316
4024
  VatResource,
3317
4025
  VatState,
3318
4026
  VatValidationAddressInput,
4027
+ WEBHOOK_ID_HEADER,
4028
+ WEBHOOK_SIGNATURE_HEADER,
4029
+ WebhookSignatureVerificationError,
4030
+ computeWebhookSignature,
4031
+ constructWebhookEvent,
3319
4032
  describeApiError,
4033
+ describeComplianceDocumentType,
3320
4034
  describeComplianceEnrollmentStatus,
4035
+ describeComplianceService,
3321
4036
  describeComplianceTaxReportState,
3322
4037
  describeComplianceTransactionState,
3323
4038
  describeComplianceTransactionType,
4039
+ describePeppolLookupStatus,
3324
4040
  describeSmartEnrichmentMode,
4041
+ describeSmartEnrichmentRejectionReason,
3325
4042
  describeSmartEnrichmentStatus,
3326
4043
  describeVatState,
4044
+ generateWebhookSignatureHeader,
3327
4045
  getBaseUrl,
3328
4046
  getFailedVatStates,
4047
+ parseWebhookSignatureHeader,
4048
+ toComplianceDocumentType,
3329
4049
  toComplianceEnrollmentStatus,
4050
+ toComplianceService,
3330
4051
  toComplianceTaxReportState,
3331
4052
  toComplianceTransactionState,
3332
4053
  toComplianceTransactionType,
4054
+ toPeppolLookupStatus,
3333
4055
  toSmartEnrichmentMode,
3334
- toSmartEnrichmentStatus
4056
+ toSmartEnrichmentRejectionReason,
4057
+ toSmartEnrichmentStatus,
4058
+ verifyWebhookSignature
3335
4059
  });
3336
4060
  //# sourceMappingURL=index.cjs.map