vesant-sdk 1.6.6 → 1.7.0-dev.e0ee6d5

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.
Files changed (86) hide show
  1. package/README.md +14 -4
  2. package/dist/{client-ePzhQKp9.d.mts → client-BolQlL5e.d.mts} +1 -1
  3. package/dist/{client-ePzhQKp9.d.ts → client-BolQlL5e.d.ts} +1 -1
  4. package/dist/client-C3DCmGe9.d.ts +436 -0
  5. package/dist/{client-C_A7QLcB.d.ts → client-DMIRx7Tu.d.mts} +5 -3
  6. package/dist/{client-BlCxjbY2.d.mts → client-DoMSYMMR.d.ts} +5 -3
  7. package/dist/client-ZNdnpWe7.d.mts +436 -0
  8. package/dist/compliance/index.d.mts +25 -429
  9. package/dist/compliance/index.d.ts +25 -429
  10. package/dist/compliance/index.js +187 -103
  11. package/dist/compliance/index.js.map +1 -1
  12. package/dist/compliance/index.mjs +187 -104
  13. package/dist/compliance/index.mjs.map +1 -1
  14. package/dist/decisions/index.d.mts +2 -2
  15. package/dist/decisions/index.d.ts +2 -2
  16. package/dist/decisions/index.js +1 -1
  17. package/dist/decisions/index.js.map +1 -1
  18. package/dist/decisions/index.mjs +1 -1
  19. package/dist/decisions/index.mjs.map +1 -1
  20. package/dist/geolocation/index.d.mts +4 -4
  21. package/dist/geolocation/index.d.ts +4 -4
  22. package/dist/geolocation/index.js +7 -24
  23. package/dist/geolocation/index.js.map +1 -1
  24. package/dist/geolocation/index.mjs +7 -24
  25. package/dist/geolocation/index.mjs.map +1 -1
  26. package/dist/index.d.mts +12 -70
  27. package/dist/index.d.ts +12 -70
  28. package/dist/index.js +294 -292
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +293 -291
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/kyc/core.d.mts +4 -4
  33. package/dist/kyc/core.d.ts +4 -4
  34. package/dist/kyc/core.js +78 -23
  35. package/dist/kyc/core.js.map +1 -1
  36. package/dist/kyc/core.mjs +78 -24
  37. package/dist/kyc/core.mjs.map +1 -1
  38. package/dist/kyc/index.d.mts +269 -45
  39. package/dist/kyc/index.d.ts +269 -45
  40. package/dist/kyc/index.js +78 -23
  41. package/dist/kyc/index.js.map +1 -1
  42. package/dist/kyc/index.mjs +78 -24
  43. package/dist/kyc/index.mjs.map +1 -1
  44. package/dist/react.d.mts +42 -7
  45. package/dist/react.d.ts +42 -7
  46. package/dist/react.js +663 -277
  47. package/dist/react.js.map +1 -1
  48. package/dist/react.mjs +663 -277
  49. package/dist/react.mjs.map +1 -1
  50. package/dist/risk-profile/index.d.mts +4 -4
  51. package/dist/risk-profile/index.d.ts +4 -4
  52. package/dist/risk-profile/index.js +1 -1
  53. package/dist/risk-profile/index.js.map +1 -1
  54. package/dist/risk-profile/index.mjs +1 -1
  55. package/dist/risk-profile/index.mjs.map +1 -1
  56. package/dist/scores/index.d.mts +2 -2
  57. package/dist/scores/index.d.ts +2 -2
  58. package/dist/scores/index.js +1 -1
  59. package/dist/scores/index.js.map +1 -1
  60. package/dist/scores/index.mjs +1 -1
  61. package/dist/scores/index.mjs.map +1 -1
  62. package/dist/tax/index.d.mts +6 -41
  63. package/dist/tax/index.d.ts +6 -41
  64. package/dist/tax/index.js +1 -36
  65. package/dist/tax/index.js.map +1 -1
  66. package/dist/tax/index.mjs +1 -36
  67. package/dist/tax/index.mjs.map +1 -1
  68. package/dist/{types-1RzYeSal.d.mts → types-BOFaMQxI.d.mts} +2 -2
  69. package/dist/{types-B4Ezqo7V.d.mts → types-CBQRNL-l.d.mts} +14 -1
  70. package/dist/{types-B4Ezqo7V.d.ts → types-CBQRNL-l.d.ts} +14 -1
  71. package/dist/{types-X5Md_dD_.d.ts → types-UGyDl1fd.d.ts} +2 -2
  72. package/dist/webhooks/index.d.mts +189 -2
  73. package/dist/webhooks/index.d.ts +189 -2
  74. package/dist/webhooks/index.js +49 -7
  75. package/dist/webhooks/index.js.map +1 -1
  76. package/dist/webhooks/index.mjs +49 -7
  77. package/dist/webhooks/index.mjs.map +1 -1
  78. package/package.json +16 -13
  79. package/dist/fraud/index.d.mts +0 -80
  80. package/dist/fraud/index.d.ts +0 -80
  81. package/dist/fraud/index.js +0 -606
  82. package/dist/fraud/index.js.map +0 -1
  83. package/dist/fraud/index.mjs +0 -604
  84. package/dist/fraud/index.mjs.map +0 -1
  85. package/dist/index-B04H4xfJ.d.mts +0 -320
  86. package/dist/index-CItMPmLL.d.ts +0 -320
@@ -1,3 +1,78 @@
1
+ // src/compliance/block-reasons.ts
2
+ var sdkReasons = {
3
+ // Jurisdiction
4
+ jurisdictionBlocked: (country, countryISO) => ({
5
+ code: "JURISDICTION_BLOCKED",
6
+ message: "Access from a blocked jurisdiction",
7
+ metadata: { country, country_iso: countryISO }
8
+ }),
9
+ jurisdictionNonCompliant: () => ({
10
+ code: "JURISDICTION_NON_COMPLIANT",
11
+ message: "Location does not meet compliance requirements"
12
+ }),
13
+ jurisdictionRegistrationDenied: () => ({
14
+ code: "JURISDICTION_REGISTRATION_DENIED",
15
+ message: "Registration is not permitted in this jurisdiction"
16
+ }),
17
+ jurisdictionRestricted: () => ({
18
+ code: "JURISDICTION_RESTRICTED",
19
+ message: "Access from a restricted jurisdiction"
20
+ }),
21
+ // Risk
22
+ riskCriticalLevel: () => ({
23
+ code: "RISK_CRITICAL_LEVEL",
24
+ message: "Risk assessment reached critical threshold"
25
+ }),
26
+ accountSuspended: () => ({
27
+ code: "RISK_ACCOUNT_SUSPENDED",
28
+ message: "Customer account is suspended"
29
+ }),
30
+ sanctionsMatch: () => ({
31
+ code: "RISK_SANCTIONS_MATCH",
32
+ message: "Customer profile matched against sanctions list"
33
+ }),
34
+ riskHighLocation: () => ({
35
+ code: "RISK_HIGH_LOCATION",
36
+ message: "High-risk geographic location"
37
+ }),
38
+ riskHighCustomer: () => ({
39
+ code: "RISK_HIGH_CUSTOMER",
40
+ message: "Customer flagged as high risk"
41
+ }),
42
+ // Device
43
+ ciphertextInvalid: () => ({
44
+ code: "DEVICE_CIPHERTEXT_INVALID",
45
+ message: "Device verification payload failed validation"
46
+ }),
47
+ gpsIPMismatch: () => ({
48
+ code: "DEVICE_GPS_IP_MISMATCH",
49
+ message: "GPS location does not match IP-derived location"
50
+ }),
51
+ gpsRequired: () => ({
52
+ code: "DEVICE_GPS_REQUIRED",
53
+ message: "GPS verification is required but was not provided"
54
+ }),
55
+ // Transaction
56
+ transactionHighAmount: (amount, currency, threshold) => ({
57
+ code: "TRANSACTION_HIGH_AMOUNT",
58
+ message: "Transaction amount exceeds high-value threshold",
59
+ metadata: { amount, currency, threshold }
60
+ }),
61
+ transactionElevatedAmount: (amount, currency, threshold) => ({
62
+ code: "TRANSACTION_ELEVATED_AMOUNT",
63
+ message: "Transaction amount exceeds elevated-value threshold",
64
+ metadata: { amount, currency, threshold }
65
+ }),
66
+ transactionJurisdictionLimit: () => ({
67
+ code: "TRANSACTION_JURISDICTION_LIMIT",
68
+ message: "Transaction amount exceeds jurisdiction limit"
69
+ }),
70
+ anonymizationDetected: () => ({
71
+ code: "NETWORK_ANONYMIZER_DETECTED",
72
+ message: "Anonymization tool detected"
73
+ })
74
+ };
75
+
1
76
  // src/core/errors.ts
2
77
  var VesantError = class _VesantError extends Error {
3
78
  constructor(message, code, statusCode, details) {
@@ -222,7 +297,7 @@ function createConsoleLogger() {
222
297
  }
223
298
 
224
299
  // src/core/version.ts
225
- var SDK_VERSION = "1.6.6";
300
+ var SDK_VERSION = "1.7.0";
226
301
 
227
302
  // src/shared/browser-utils.ts
228
303
  function generateUUID() {
@@ -725,7 +800,7 @@ function encodePayload(payload) {
725
800
  } else if (typeof Buffer !== "undefined") {
726
801
  return Buffer.from(json, "utf-8").toString("base64");
727
802
  }
728
- throw new Error("No base64 encoding method available");
803
+ throw new VesantError("No base64 encoding method available", "BASE64_UNAVAILABLE");
729
804
  }
730
805
  async function generateCipherText(options, config) {
731
806
  const warnings = [];
@@ -750,8 +825,9 @@ async function generateCipherText(options, config) {
750
825
  if (location) {
751
826
  locationData = location;
752
827
  } else if (gpsRequiredByConfig) {
753
- throw new Error(
754
- `GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`
828
+ throw new VesantError(
829
+ `GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
830
+ "GPS_REQUIRED"
755
831
  );
756
832
  } else {
757
833
  warnings.push("GPS location not available or permission denied");
@@ -845,10 +921,9 @@ var GeolocationClient = class extends BaseClient {
845
921
  if (!request.ip_address?.trim()) {
846
922
  throw new ValidationError("ip_address is required and must be a non-empty string", ["ip_address"]);
847
923
  }
848
- const enrichedRequest = request.device_fingerprint ? request : { ...request, device_fingerprint: collectDeviceFingerprint() };
849
924
  return this.requestWithRetry("/api/v1/geo/verify", {
850
925
  method: "POST",
851
- body: JSON.stringify(enrichedRequest)
926
+ body: JSON.stringify(request)
852
927
  }, void 0, void 0, requestOptions);
853
928
  }
854
929
  /**
@@ -1073,6 +1148,7 @@ var GeolocationClient = class extends BaseClient {
1073
1148
  risk_level: risk.level ?? "low",
1074
1149
  risk_score: risk.score ?? 0,
1075
1150
  risk_reasons: risk.is_blocked && risk.block_reasons ? risk.block_reasons : risk.factors ?? [],
1151
+ risk_reasons_structured: risk.block_reasons_structured ?? [],
1076
1152
  jurisdiction: cipherTextResult.jurisdiction,
1077
1153
  geofence_evaluation: cipherTextResult.geofence_evaluation,
1078
1154
  record_id: cipherTextResult.record_id ?? "",
@@ -1252,24 +1328,6 @@ var GeolocationClient = class extends BaseClient {
1252
1328
  // Utility Methods (inherited from BaseClient: healthCheck, updateConfig, getConfig, buildQueryString)
1253
1329
  // ============================================================================
1254
1330
  };
1255
- function collectDeviceFingerprint() {
1256
- const deviceId = generateDeviceId();
1257
- const browserInfo = getBrowserInfo();
1258
- const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "server";
1259
- const platform = typeof navigator !== "undefined" ? navigator.platform || "unknown" : "server";
1260
- return {
1261
- device_id: deviceId,
1262
- user_agent: userAgent,
1263
- platform,
1264
- browser: browserInfo.browser,
1265
- browser_version: browserInfo.browser_version,
1266
- os: browserInfo.os,
1267
- os_version: browserInfo.os_version,
1268
- screen_resolution: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0,
1269
- language: typeof navigator !== "undefined" ? navigator.language : void 0,
1270
- timezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone
1271
- };
1272
- }
1273
1331
 
1274
1332
  // src/risk-profile/client.ts
1275
1333
  var RiskProfileClient = class extends BaseClient {
@@ -1532,10 +1590,10 @@ var ComplianceClient = class {
1532
1590
  device_fingerprint: request.deviceFingerprint
1533
1591
  }, requestOptions);
1534
1592
  }
1535
- const blockReasons = this.evaluateRegistrationBlock(geoVerification, cipherTextResult);
1536
- if (blockReasons.length > 0) {
1593
+ const structuredBlockReasons = this.evaluateRegistrationBlock(geoVerification, cipherTextResult);
1594
+ if (structuredBlockReasons.length > 0) {
1537
1595
  if (this.config.debug) {
1538
- this.logger.debug("Registration blocked at geo stage", { blockReasons });
1596
+ this.logger.debug("Registration blocked at geo stage", { blockReasons: structuredBlockReasons });
1539
1597
  }
1540
1598
  return {
1541
1599
  allowed: false,
@@ -1543,9 +1601,7 @@ var ComplianceClient = class {
1543
1601
  profile: null,
1544
1602
  requiresKYC: false,
1545
1603
  requiresEDD: false,
1546
- blockReasons,
1547
- processingTime: Date.now() - startTime,
1548
- cipherTextValidation: cipherTextResult
1604
+ ...this.buildReasonTail(structuredBlockReasons, startTime, cipherTextResult)
1549
1605
  };
1550
1606
  }
1551
1607
  const profile = await this.riskClient.createProfile({
@@ -1579,14 +1635,12 @@ var ComplianceClient = class {
1579
1635
  profile,
1580
1636
  requiresKYC,
1581
1637
  requiresEDD,
1582
- blockReasons: [],
1583
- processingTime: Date.now() - startTime,
1584
- cipherTextValidation: cipherTextResult
1638
+ ...this.buildReasonTail([], startTime, cipherTextResult)
1585
1639
  };
1586
1640
  } catch (error) {
1587
1641
  if (this.config.debug) {
1588
1642
  this.logger.error("Registration verification failed", {
1589
- code: error instanceof Error ? error.code : void 0,
1643
+ code: error instanceof VesantError ? error.code : void 0,
1590
1644
  message: error instanceof Error ? error.message : "Unknown error"
1591
1645
  });
1592
1646
  }
@@ -1615,52 +1669,62 @@ var ComplianceClient = class {
1615
1669
  evaluateRegistrationBlock(geoVerification, cipherTextResult) {
1616
1670
  const blockReasons = [];
1617
1671
  if (geoVerification.is_blocked) {
1618
- blockReasons.push(...geoVerification.risk_reasons ?? []);
1672
+ blockReasons.push(...geoVerification.risk_reasons_structured ?? []);
1619
1673
  }
1620
1674
  if (!geoVerification.is_compliant) {
1621
- if (!blockReasons.includes("non_compliant_jurisdiction")) {
1622
- blockReasons.push("non_compliant_jurisdiction");
1675
+ if (!blockReasons.some((r) => r.code === "JURISDICTION_NON_COMPLIANT")) {
1676
+ blockReasons.push(sdkReasons.jurisdictionNonCompliant());
1623
1677
  }
1624
1678
  }
1625
1679
  const jurisdiction = geoVerification.jurisdiction;
1626
1680
  if (jurisdiction) {
1627
1681
  if (jurisdiction.allow_registration === false) {
1628
- blockReasons.push("registration_not_allowed_in_jurisdiction");
1682
+ blockReasons.push(sdkReasons.jurisdictionRegistrationDenied());
1629
1683
  }
1630
1684
  if (jurisdiction.status === "blocked" || jurisdiction.status === "sanctioned") {
1631
- if (!blockReasons.includes("blocked_jurisdiction")) {
1632
- blockReasons.push("blocked_jurisdiction");
1685
+ if (!blockReasons.some((r) => r.code === "JURISDICTION_BLOCKED")) {
1686
+ blockReasons.push(sdkReasons.jurisdictionBlocked(jurisdiction.country_name ?? "", jurisdiction.country_iso ?? ""));
1633
1687
  }
1634
1688
  }
1635
1689
  if (jurisdiction.status === "restricted" && jurisdiction.allow_registration === false) {
1636
- if (!blockReasons.includes("restricted_jurisdiction_no_registration")) {
1637
- blockReasons.push("restricted_jurisdiction_no_registration");
1690
+ if (!blockReasons.some((r) => r.code === "JURISDICTION_RESTRICTED")) {
1691
+ blockReasons.push(sdkReasons.jurisdictionRestricted());
1638
1692
  }
1639
1693
  }
1640
1694
  }
1641
1695
  if (geoVerification.geofence_evaluation?.blocked) {
1642
- blockReasons.push(...geoVerification.geofence_evaluation.reasons ?? []);
1696
+ blockReasons.push(
1697
+ ...(geoVerification.geofence_evaluation.reasons ?? []).map((m) => ({
1698
+ code: "JURISDICTION_GEOFENCE_VIOLATION",
1699
+ message: m
1700
+ }))
1701
+ );
1643
1702
  }
1644
1703
  if (geoVerification.risk_level === "critical") {
1645
- if (!blockReasons.includes("critical_risk_level")) {
1646
- blockReasons.push("critical_risk_level");
1704
+ if (!blockReasons.some((r) => r.code === "RISK_CRITICAL_LEVEL")) {
1705
+ blockReasons.push(sdkReasons.riskCriticalLevel());
1647
1706
  }
1648
1707
  }
1649
1708
  if (cipherTextResult) {
1650
1709
  if (!cipherTextResult.valid) {
1651
- blockReasons.push("ciphertext_validation_failed");
1710
+ blockReasons.push(sdkReasons.ciphertextInvalid());
1652
1711
  }
1653
1712
  if (cipherTextResult.risk?.is_blocked) {
1654
- blockReasons.push(...cipherTextResult.risk.block_reasons || []);
1713
+ blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
1655
1714
  }
1656
1715
  if (cipherTextResult.risk?.location_mismatch) {
1657
- blockReasons.push("gps_ip_location_mismatch");
1716
+ blockReasons.push(sdkReasons.gpsIPMismatch());
1658
1717
  }
1659
1718
  }
1660
1719
  if (geoVerification.gps_required && !cipherTextResult) {
1661
- blockReasons.push("gps_verification_required");
1720
+ blockReasons.push(sdkReasons.gpsRequired());
1662
1721
  }
1663
- return [...new Set(blockReasons)];
1722
+ const seen = /* @__PURE__ */ new Set();
1723
+ return blockReasons.filter((r) => {
1724
+ if (seen.has(r.code)) return false;
1725
+ seen.add(r.code);
1726
+ return true;
1727
+ });
1664
1728
  }
1665
1729
  /**
1666
1730
  * Validate registration request has all required fields
@@ -1831,9 +1895,7 @@ var ComplianceClient = class {
1831
1895
  geolocation: geoVerification,
1832
1896
  profile: null,
1833
1897
  requiresStepUp: false,
1834
- blockReasons: loginBlockReasons,
1835
- processingTime: Date.now() - startTime,
1836
- cipherTextValidation: cipherTextResult
1898
+ ...this.buildReasonTail(loginBlockReasons, startTime, cipherTextResult)
1837
1899
  };
1838
1900
  }
1839
1901
  let profile;
@@ -1853,14 +1915,13 @@ var ComplianceClient = class {
1853
1915
  profile = await this.createProfileFromGeo(request.customerId, geoVerification);
1854
1916
  }
1855
1917
  const requiresStepUp = geoVerification.risk_level === "high" || geoVerification.risk_level === "critical" || cipherTextResult?.risk?.location_mismatch === true;
1918
+ const loginFinalStructured = isBlocked || profile.customer_status === "suspended" ? this.getBlockReasons(geoVerification, profile, cipherTextResult) : [];
1856
1919
  return {
1857
1920
  allowed: !isBlocked && profile.customer_status !== "suspended",
1858
1921
  geolocation: geoVerification,
1859
1922
  profile,
1860
1923
  requiresStepUp,
1861
- blockReasons: isBlocked || profile.customer_status === "suspended" ? this.getBlockReasons(geoVerification, profile, cipherTextResult) : [],
1862
- processingTime: Date.now() - startTime,
1863
- cipherTextValidation: cipherTextResult
1924
+ ...this.buildReasonTail(loginFinalStructured, startTime, cipherTextResult)
1864
1925
  };
1865
1926
  } catch (error) {
1866
1927
  throw new ComplianceError("Login verification failed", error instanceof Error ? error.message : void 0);
@@ -1942,20 +2003,19 @@ var ComplianceClient = class {
1942
2003
  }
1943
2004
  const geoBlocked = !geoVerification.is_compliant || geoVerification.is_blocked || !!cipherTextResult?.risk?.is_blocked || cipherTextResult?.valid === false || geoVerification.gps_required && !cipherTextResult;
1944
2005
  if (geoBlocked && !profileResult) {
2006
+ const earlyStructured = this.getTransactionBlockReasons(
2007
+ geoVerification,
2008
+ { score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
2009
+ true,
2010
+ cipherTextResult
2011
+ );
1945
2012
  return {
1946
2013
  allowed: false,
1947
2014
  geolocation: geoVerification,
1948
2015
  profile: null,
1949
2016
  transactionRisk: { score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
1950
2017
  requiresApproval: false,
1951
- blockReasons: this.getTransactionBlockReasons(
1952
- geoVerification,
1953
- { score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
1954
- true,
1955
- cipherTextResult
1956
- ),
1957
- processingTime: Date.now() - startTime,
1958
- cipherTextValidation: cipherTextResult
2018
+ ...this.buildReasonTail(earlyStructured, startTime, cipherTextResult)
1959
2019
  };
1960
2020
  }
1961
2021
  if (!profileResult) {
@@ -1975,20 +2035,19 @@ var ComplianceClient = class {
1975
2035
  geoVerification.jurisdiction
1976
2036
  );
1977
2037
  const isAllowed = !geoBlocked && jurisdictionAllowed && transactionRisk.allowed && profile.customer_status !== "suspended";
2038
+ const txStructured = this.getTransactionBlockReasons(
2039
+ geoVerification,
2040
+ transactionRisk,
2041
+ jurisdictionAllowed,
2042
+ cipherTextResult
2043
+ );
1978
2044
  return {
1979
2045
  allowed: isAllowed,
1980
2046
  geolocation: geoVerification,
1981
2047
  profile,
1982
2048
  transactionRisk,
1983
2049
  requiresApproval: transactionRisk.requiresManualReview,
1984
- blockReasons: this.getTransactionBlockReasons(
1985
- geoVerification,
1986
- transactionRisk,
1987
- jurisdictionAllowed,
1988
- cipherTextResult
1989
- ),
1990
- processingTime: Date.now() - startTime,
1991
- cipherTextValidation: cipherTextResult
2050
+ ...this.buildReasonTail(txStructured, startTime, cipherTextResult)
1992
2051
  };
1993
2052
  } catch (error) {
1994
2053
  throw new ComplianceError("Transaction verification failed", error instanceof Error ? error.message : void 0);
@@ -2041,30 +2100,42 @@ var ComplianceClient = class {
2041
2100
  }
2042
2101
  }
2043
2102
  const cipherTextBlocked = cipherTextResult ? !cipherTextResult.valid || cipherTextResult.risk?.is_blocked === true : false;
2044
- const blockReasons = [...geoVerification.risk_reasons ?? []];
2103
+ const structuredEventReasons = [...geoVerification.risk_reasons_structured ?? []];
2045
2104
  if (cipherTextResult) {
2046
2105
  if (!cipherTextResult.valid) {
2047
- blockReasons.push("ciphertext_validation_failed");
2106
+ structuredEventReasons.push(sdkReasons.ciphertextInvalid());
2048
2107
  }
2049
2108
  if (cipherTextResult.risk?.is_blocked) {
2050
- blockReasons.push(...cipherTextResult.risk.block_reasons || []);
2109
+ structuredEventReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
2051
2110
  }
2052
2111
  }
2053
2112
  const gpsBlocked = geoVerification.gps_required && !cipherTextResult;
2054
2113
  if (gpsBlocked) {
2055
- blockReasons.push("gps_verification_required");
2114
+ structuredEventReasons.push(sdkReasons.gpsRequired());
2056
2115
  }
2116
+ const seen = /* @__PURE__ */ new Set();
2117
+ const dedupedStructured = structuredEventReasons.filter((r) => {
2118
+ if (seen.has(r.code)) return false;
2119
+ seen.add(r.code);
2120
+ return true;
2121
+ });
2057
2122
  return {
2058
2123
  allowed: geoVerification.is_compliant && !geoVerification.is_blocked && !cipherTextBlocked && !gpsBlocked,
2059
2124
  geolocation: geoVerification,
2060
- blockReasons: [...new Set(blockReasons)],
2061
- processingTime: Date.now() - startTime,
2062
- cipherTextValidation: cipherTextResult
2125
+ ...this.buildReasonTail(dedupedStructured, startTime, cipherTextResult)
2063
2126
  };
2064
2127
  }
2065
2128
  // ============================================================================
2066
2129
  // Helper Methods
2067
2130
  // ============================================================================
2131
+ buildReasonTail(structured, startTime, cipherTextResult) {
2132
+ return {
2133
+ blockReasons: structured.map((r) => r.message),
2134
+ blockReasonsStructured: structured,
2135
+ processingTime: Date.now() - startTime,
2136
+ cipherTextValidation: cipherTextResult
2137
+ };
2138
+ }
2068
2139
  shouldUpdateProfile(profile, newCity) {
2069
2140
  return !profile.location || !profile.location.includes(newCity);
2070
2141
  }
@@ -2141,7 +2212,8 @@ var ComplianceClient = class {
2141
2212
  is_blocked: risk.is_blocked,
2142
2213
  risk_level: risk.level,
2143
2214
  risk_score: risk.score,
2144
- risk_reasons: risk.is_blocked && risk.block_reasons ? risk.block_reasons : risk.factors ?? [],
2215
+ risk_reasons: [],
2216
+ risk_reasons_structured: risk.is_blocked && risk.block_reasons_structured ? risk.block_reasons_structured : [],
2145
2217
  jurisdiction: ct.jurisdiction,
2146
2218
  geofence_evaluation: ct.geofence_evaluation,
2147
2219
  record_id: ct.record_id ?? "",
@@ -2156,35 +2228,35 @@ var ComplianceClient = class {
2156
2228
  );
2157
2229
  }
2158
2230
  let riskScore = 0;
2159
- const factors = [];
2231
+ const structuredFactors = [];
2160
2232
  const normalizedAmount = this.normalizeToUSD(amount, currency);
2161
2233
  if (normalizedAmount > 1e4) {
2162
2234
  riskScore += 30;
2163
- factors.push("high_transaction_amount");
2235
+ structuredFactors.push(sdkReasons.transactionHighAmount(normalizedAmount, currency, 1e4));
2164
2236
  } else if (normalizedAmount > 5e3) {
2165
2237
  riskScore += 15;
2166
- factors.push("elevated_transaction_amount");
2238
+ structuredFactors.push(sdkReasons.transactionElevatedAmount(normalizedAmount, currency, 5e3));
2167
2239
  }
2168
2240
  riskScore += geoVerification.risk_score * 0.4;
2169
2241
  if (geoVerification.risk_level === "high" || geoVerification.risk_level === "critical") {
2170
- factors.push("high_risk_location");
2242
+ structuredFactors.push(sdkReasons.riskHighLocation());
2171
2243
  }
2172
2244
  riskScore += profile.risk_score * 0.3;
2173
2245
  if (profile.risk_category === "high" || profile.risk_category === "critical") {
2174
- factors.push("high_risk_customer");
2246
+ structuredFactors.push(sdkReasons.riskHighCustomer());
2175
2247
  }
2176
2248
  if (geoVerification.location.is_vpn || geoVerification.location.is_proxy) {
2177
2249
  riskScore += 20;
2178
- factors.push("anonymization_detected");
2250
+ structuredFactors.push(sdkReasons.anonymizationDetected());
2179
2251
  }
2180
2252
  if (profile.customer_status === "suspended") {
2181
2253
  riskScore += 50;
2182
- factors.push("account_suspended");
2254
+ structuredFactors.push(sdkReasons.accountSuspended());
2183
2255
  }
2184
2256
  if (cipherTextResult) {
2185
2257
  if (cipherTextResult.risk?.location_mismatch) {
2186
2258
  riskScore += 20;
2187
- factors.push("gps_ip_location_mismatch");
2259
+ structuredFactors.push(sdkReasons.gpsIPMismatch());
2188
2260
  }
2189
2261
  if (cipherTextResult.risk?.score) {
2190
2262
  riskScore += cipherTextResult.risk.score * 0.2;
@@ -2193,7 +2265,8 @@ var ComplianceClient = class {
2193
2265
  return {
2194
2266
  score: Math.min(riskScore, 100),
2195
2267
  level: this.getRiskLevel(riskScore),
2196
- factors,
2268
+ factors: structuredFactors.map((r) => r.message),
2269
+ factorsStructured: structuredFactors,
2197
2270
  allowed: riskScore < 70,
2198
2271
  requiresManualReview: riskScore >= 60 && riskScore < 70
2199
2272
  };
@@ -2218,52 +2291,62 @@ var ComplianceClient = class {
2218
2291
  getBlockReasons(geoVerification, profile, cipherTextResult) {
2219
2292
  const reasons = [];
2220
2293
  if (geoVerification.is_blocked) {
2221
- reasons.push(...geoVerification.risk_reasons ?? []);
2294
+ reasons.push(...geoVerification.risk_reasons_structured ?? []);
2222
2295
  }
2223
2296
  if (profile) {
2224
2297
  if (profile.customer_status === "suspended") {
2225
- reasons.push("account_suspended");
2298
+ reasons.push(sdkReasons.accountSuspended());
2226
2299
  }
2227
2300
  if (profile.has_sanctions) {
2228
- reasons.push("sanctions_match");
2301
+ reasons.push(sdkReasons.sanctionsMatch());
2229
2302
  }
2230
2303
  }
2231
2304
  if (cipherTextResult) {
2232
2305
  if (!cipherTextResult.valid) {
2233
- reasons.push("ciphertext_validation_failed");
2306
+ reasons.push(sdkReasons.ciphertextInvalid());
2234
2307
  }
2235
2308
  if (cipherTextResult.risk?.is_blocked) {
2236
- reasons.push(...cipherTextResult.risk.block_reasons || []);
2309
+ reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
2237
2310
  }
2238
2311
  }
2239
2312
  if (geoVerification.gps_required && !cipherTextResult) {
2240
- reasons.push("gps_verification_required");
2313
+ reasons.push(sdkReasons.gpsRequired());
2241
2314
  }
2242
- return [...new Set(reasons)];
2315
+ const seen = /* @__PURE__ */ new Set();
2316
+ return reasons.filter((r) => {
2317
+ if (seen.has(r.code)) return false;
2318
+ seen.add(r.code);
2319
+ return true;
2320
+ });
2243
2321
  }
2244
2322
  getTransactionBlockReasons(geoVerification, transactionRisk, jurisdictionAllowed, cipherTextResult) {
2245
2323
  const reasons = [];
2246
2324
  if (!geoVerification.is_compliant) {
2247
- reasons.push("non_compliant_jurisdiction");
2325
+ reasons.push(sdkReasons.jurisdictionNonCompliant());
2248
2326
  }
2249
2327
  if (!jurisdictionAllowed) {
2250
- reasons.push("exceeds_jurisdiction_limit");
2328
+ reasons.push(sdkReasons.transactionJurisdictionLimit());
2251
2329
  }
2252
2330
  if (!transactionRisk.allowed) {
2253
- reasons.push(...transactionRisk.factors);
2331
+ reasons.push(...transactionRisk.factorsStructured ?? []);
2254
2332
  }
2255
2333
  if (cipherTextResult) {
2256
2334
  if (!cipherTextResult.valid) {
2257
- reasons.push("ciphertext_validation_failed");
2335
+ reasons.push(sdkReasons.ciphertextInvalid());
2258
2336
  }
2259
2337
  if (cipherTextResult.risk?.is_blocked) {
2260
- reasons.push(...cipherTextResult.risk.block_reasons || []);
2338
+ reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
2261
2339
  }
2262
2340
  }
2263
2341
  if (geoVerification.gps_required && !cipherTextResult) {
2264
- reasons.push("gps_verification_required");
2342
+ reasons.push(sdkReasons.gpsRequired());
2265
2343
  }
2266
- return [...new Set(reasons)];
2344
+ const seen = /* @__PURE__ */ new Set();
2345
+ return reasons.filter((r) => {
2346
+ if (seen.has(r.code)) return false;
2347
+ seen.add(r.code);
2348
+ return true;
2349
+ });
2267
2350
  }
2268
2351
  // ============================================================================
2269
2352
  // Location Request Methods
@@ -2481,6 +2564,6 @@ var ComplianceClient = class {
2481
2564
  }
2482
2565
  };
2483
2566
 
2484
- export { ComplianceClient, DEFAULT_CURRENCY_RATES };
2567
+ export { ComplianceClient, DEFAULT_CURRENCY_RATES, sdkReasons };
2485
2568
  //# sourceMappingURL=index.mjs.map
2486
2569
  //# sourceMappingURL=index.mjs.map