vesant-sdk 1.6.6 → 1.7.0-dev.117915b
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/README.md +14 -4
- package/dist/client-B0qhE2kr.d.mts +436 -0
- package/dist/{client-ePzhQKp9.d.mts → client-BolQlL5e.d.mts} +1 -1
- package/dist/{client-ePzhQKp9.d.ts → client-BolQlL5e.d.ts} +1 -1
- package/dist/{client-BlCxjbY2.d.mts → client-DF7hlMEz.d.ts} +18 -3
- package/dist/{client-C_A7QLcB.d.ts → client-DrjgZoH_.d.mts} +18 -3
- package/dist/client-DtH2RLuy.d.ts +436 -0
- package/dist/compliance/index.d.mts +25 -429
- package/dist/compliance/index.d.ts +25 -429
- package/dist/compliance/index.js +186 -105
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +186 -106
- package/dist/compliance/index.mjs.map +1 -1
- package/dist/decisions/index.d.mts +2 -2
- package/dist/decisions/index.d.ts +2 -2
- package/dist/decisions/index.js +1 -1
- package/dist/decisions/index.js.map +1 -1
- package/dist/decisions/index.mjs +1 -1
- package/dist/decisions/index.mjs.map +1 -1
- package/dist/geolocation/index.d.mts +4 -4
- package/dist/geolocation/index.d.ts +4 -4
- package/dist/geolocation/index.js +7 -24
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +7 -24
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index.d.mts +12 -70
- package/dist/index.d.ts +12 -70
- package/dist/index.js +305 -299
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +304 -298
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +4 -4
- package/dist/kyc/core.d.ts +4 -4
- package/dist/kyc/core.js +86 -27
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +86 -28
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +309 -50
- package/dist/kyc/index.d.ts +309 -50
- package/dist/kyc/index.js +86 -27
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +86 -28
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +48 -9
- package/dist/react.d.ts +48 -9
- package/dist/react.js +930 -276
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +929 -275
- package/dist/react.mjs.map +1 -1
- package/dist/risk-profile/index.d.mts +4 -4
- package/dist/risk-profile/index.d.ts +4 -4
- package/dist/risk-profile/index.js +1 -1
- package/dist/risk-profile/index.js.map +1 -1
- package/dist/risk-profile/index.mjs +1 -1
- package/dist/risk-profile/index.mjs.map +1 -1
- package/dist/scores/index.d.mts +2 -2
- package/dist/scores/index.d.ts +2 -2
- package/dist/scores/index.js +1 -1
- package/dist/scores/index.js.map +1 -1
- package/dist/scores/index.mjs +1 -1
- package/dist/scores/index.mjs.map +1 -1
- package/dist/tax/index.d.mts +23 -41
- package/dist/tax/index.d.ts +23 -41
- package/dist/tax/index.js +5 -37
- package/dist/tax/index.js.map +1 -1
- package/dist/tax/index.mjs +5 -37
- package/dist/tax/index.mjs.map +1 -1
- package/dist/{types-1RzYeSal.d.mts → types-BOFaMQxI.d.mts} +2 -2
- package/dist/{types-B4Ezqo7V.d.mts → types-CBQRNL-l.d.mts} +14 -1
- package/dist/{types-B4Ezqo7V.d.ts → types-CBQRNL-l.d.ts} +14 -1
- package/dist/{types-X5Md_dD_.d.ts → types-UGyDl1fd.d.ts} +2 -2
- package/dist/webhooks/index.d.mts +189 -2
- package/dist/webhooks/index.d.ts +189 -2
- package/dist/webhooks/index.js +49 -7
- package/dist/webhooks/index.js.map +1 -1
- package/dist/webhooks/index.mjs +49 -7
- package/dist/webhooks/index.mjs.map +1 -1
- package/package.json +16 -13
- package/dist/fraud/index.d.mts +0 -80
- package/dist/fraud/index.d.ts +0 -80
- package/dist/fraud/index.js +0 -606
- package/dist/fraud/index.js.map +0 -1
- package/dist/fraud/index.mjs +0 -604
- package/dist/fraud/index.mjs.map +0 -1
- package/dist/index-B04H4xfJ.d.mts +0 -320
- 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.
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
1536
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,59 @@ var ComplianceClient = class {
|
|
|
1615
1669
|
evaluateRegistrationBlock(geoVerification, cipherTextResult) {
|
|
1616
1670
|
const blockReasons = [];
|
|
1617
1671
|
if (geoVerification.is_blocked) {
|
|
1618
|
-
blockReasons.push(...geoVerification.
|
|
1672
|
+
blockReasons.push(...geoVerification.risk_reasons_structured ?? []);
|
|
1619
1673
|
}
|
|
1620
1674
|
if (!geoVerification.is_compliant) {
|
|
1621
|
-
if (!blockReasons.
|
|
1622
|
-
blockReasons.push(
|
|
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(
|
|
1682
|
+
blockReasons.push(sdkReasons.jurisdictionRegistrationDenied());
|
|
1629
1683
|
}
|
|
1630
1684
|
if (jurisdiction.status === "blocked" || jurisdiction.status === "sanctioned") {
|
|
1631
|
-
if (!blockReasons.
|
|
1632
|
-
blockReasons.push("
|
|
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.
|
|
1637
|
-
blockReasons.push(
|
|
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(
|
|
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.
|
|
1646
|
-
blockReasons.push(
|
|
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(
|
|
1710
|
+
blockReasons.push(sdkReasons.ciphertextInvalid());
|
|
1652
1711
|
}
|
|
1653
1712
|
if (cipherTextResult.risk?.is_blocked) {
|
|
1654
|
-
blockReasons.push(...cipherTextResult.risk.
|
|
1655
|
-
}
|
|
1656
|
-
if (cipherTextResult.risk?.location_mismatch) {
|
|
1657
|
-
blockReasons.push("gps_ip_location_mismatch");
|
|
1713
|
+
blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
1658
1714
|
}
|
|
1659
1715
|
}
|
|
1660
1716
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
1661
|
-
blockReasons.push(
|
|
1717
|
+
blockReasons.push(sdkReasons.gpsRequired());
|
|
1662
1718
|
}
|
|
1663
|
-
|
|
1719
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1720
|
+
return blockReasons.filter((r) => {
|
|
1721
|
+
if (seen.has(r.code)) return false;
|
|
1722
|
+
seen.add(r.code);
|
|
1723
|
+
return true;
|
|
1724
|
+
});
|
|
1664
1725
|
}
|
|
1665
1726
|
/**
|
|
1666
1727
|
* Validate registration request has all required fields
|
|
@@ -1831,9 +1892,7 @@ var ComplianceClient = class {
|
|
|
1831
1892
|
geolocation: geoVerification,
|
|
1832
1893
|
profile: null,
|
|
1833
1894
|
requiresStepUp: false,
|
|
1834
|
-
|
|
1835
|
-
processingTime: Date.now() - startTime,
|
|
1836
|
-
cipherTextValidation: cipherTextResult
|
|
1895
|
+
...this.buildReasonTail(loginBlockReasons, startTime, cipherTextResult)
|
|
1837
1896
|
};
|
|
1838
1897
|
}
|
|
1839
1898
|
let profile;
|
|
@@ -1853,14 +1912,13 @@ var ComplianceClient = class {
|
|
|
1853
1912
|
profile = await this.createProfileFromGeo(request.customerId, geoVerification);
|
|
1854
1913
|
}
|
|
1855
1914
|
const requiresStepUp = geoVerification.risk_level === "high" || geoVerification.risk_level === "critical" || cipherTextResult?.risk?.location_mismatch === true;
|
|
1915
|
+
const loginFinalStructured = isBlocked || profile.customer_status === "suspended" ? this.getBlockReasons(geoVerification, profile, cipherTextResult) : [];
|
|
1856
1916
|
return {
|
|
1857
1917
|
allowed: !isBlocked && profile.customer_status !== "suspended",
|
|
1858
1918
|
geolocation: geoVerification,
|
|
1859
1919
|
profile,
|
|
1860
1920
|
requiresStepUp,
|
|
1861
|
-
|
|
1862
|
-
processingTime: Date.now() - startTime,
|
|
1863
|
-
cipherTextValidation: cipherTextResult
|
|
1921
|
+
...this.buildReasonTail(loginFinalStructured, startTime, cipherTextResult)
|
|
1864
1922
|
};
|
|
1865
1923
|
} catch (error) {
|
|
1866
1924
|
throw new ComplianceError("Login verification failed", error instanceof Error ? error.message : void 0);
|
|
@@ -1942,20 +2000,19 @@ var ComplianceClient = class {
|
|
|
1942
2000
|
}
|
|
1943
2001
|
const geoBlocked = !geoVerification.is_compliant || geoVerification.is_blocked || !!cipherTextResult?.risk?.is_blocked || cipherTextResult?.valid === false || geoVerification.gps_required && !cipherTextResult;
|
|
1944
2002
|
if (geoBlocked && !profileResult) {
|
|
2003
|
+
const earlyStructured = this.getTransactionBlockReasons(
|
|
2004
|
+
geoVerification,
|
|
2005
|
+
{ score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
|
|
2006
|
+
true,
|
|
2007
|
+
cipherTextResult
|
|
2008
|
+
);
|
|
1945
2009
|
return {
|
|
1946
2010
|
allowed: false,
|
|
1947
2011
|
geolocation: geoVerification,
|
|
1948
2012
|
profile: null,
|
|
1949
2013
|
transactionRisk: { score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
|
|
1950
2014
|
requiresApproval: false,
|
|
1951
|
-
|
|
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
|
|
2015
|
+
...this.buildReasonTail(earlyStructured, startTime, cipherTextResult)
|
|
1959
2016
|
};
|
|
1960
2017
|
}
|
|
1961
2018
|
if (!profileResult) {
|
|
@@ -1975,20 +2032,19 @@ var ComplianceClient = class {
|
|
|
1975
2032
|
geoVerification.jurisdiction
|
|
1976
2033
|
);
|
|
1977
2034
|
const isAllowed = !geoBlocked && jurisdictionAllowed && transactionRisk.allowed && profile.customer_status !== "suspended";
|
|
2035
|
+
const txStructured = this.getTransactionBlockReasons(
|
|
2036
|
+
geoVerification,
|
|
2037
|
+
transactionRisk,
|
|
2038
|
+
jurisdictionAllowed,
|
|
2039
|
+
cipherTextResult
|
|
2040
|
+
);
|
|
1978
2041
|
return {
|
|
1979
2042
|
allowed: isAllowed,
|
|
1980
2043
|
geolocation: geoVerification,
|
|
1981
2044
|
profile,
|
|
1982
2045
|
transactionRisk,
|
|
1983
2046
|
requiresApproval: transactionRisk.requiresManualReview,
|
|
1984
|
-
|
|
1985
|
-
geoVerification,
|
|
1986
|
-
transactionRisk,
|
|
1987
|
-
jurisdictionAllowed,
|
|
1988
|
-
cipherTextResult
|
|
1989
|
-
),
|
|
1990
|
-
processingTime: Date.now() - startTime,
|
|
1991
|
-
cipherTextValidation: cipherTextResult
|
|
2047
|
+
...this.buildReasonTail(txStructured, startTime, cipherTextResult)
|
|
1992
2048
|
};
|
|
1993
2049
|
} catch (error) {
|
|
1994
2050
|
throw new ComplianceError("Transaction verification failed", error instanceof Error ? error.message : void 0);
|
|
@@ -2041,30 +2097,42 @@ var ComplianceClient = class {
|
|
|
2041
2097
|
}
|
|
2042
2098
|
}
|
|
2043
2099
|
const cipherTextBlocked = cipherTextResult ? !cipherTextResult.valid || cipherTextResult.risk?.is_blocked === true : false;
|
|
2044
|
-
const
|
|
2100
|
+
const structuredEventReasons = [...geoVerification.risk_reasons_structured ?? []];
|
|
2045
2101
|
if (cipherTextResult) {
|
|
2046
2102
|
if (!cipherTextResult.valid) {
|
|
2047
|
-
|
|
2103
|
+
structuredEventReasons.push(sdkReasons.ciphertextInvalid());
|
|
2048
2104
|
}
|
|
2049
2105
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2050
|
-
|
|
2106
|
+
structuredEventReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2051
2107
|
}
|
|
2052
2108
|
}
|
|
2053
2109
|
const gpsBlocked = geoVerification.gps_required && !cipherTextResult;
|
|
2054
2110
|
if (gpsBlocked) {
|
|
2055
|
-
|
|
2111
|
+
structuredEventReasons.push(sdkReasons.gpsRequired());
|
|
2056
2112
|
}
|
|
2113
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2114
|
+
const dedupedStructured = structuredEventReasons.filter((r) => {
|
|
2115
|
+
if (seen.has(r.code)) return false;
|
|
2116
|
+
seen.add(r.code);
|
|
2117
|
+
return true;
|
|
2118
|
+
});
|
|
2057
2119
|
return {
|
|
2058
2120
|
allowed: geoVerification.is_compliant && !geoVerification.is_blocked && !cipherTextBlocked && !gpsBlocked,
|
|
2059
2121
|
geolocation: geoVerification,
|
|
2060
|
-
|
|
2061
|
-
processingTime: Date.now() - startTime,
|
|
2062
|
-
cipherTextValidation: cipherTextResult
|
|
2122
|
+
...this.buildReasonTail(dedupedStructured, startTime, cipherTextResult)
|
|
2063
2123
|
};
|
|
2064
2124
|
}
|
|
2065
2125
|
// ============================================================================
|
|
2066
2126
|
// Helper Methods
|
|
2067
2127
|
// ============================================================================
|
|
2128
|
+
buildReasonTail(structured, startTime, cipherTextResult) {
|
|
2129
|
+
return {
|
|
2130
|
+
blockReasons: structured.map((r) => r.message),
|
|
2131
|
+
blockReasonsStructured: structured,
|
|
2132
|
+
processingTime: Date.now() - startTime,
|
|
2133
|
+
cipherTextValidation: cipherTextResult
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2068
2136
|
shouldUpdateProfile(profile, newCity) {
|
|
2069
2137
|
return !profile.location || !profile.location.includes(newCity);
|
|
2070
2138
|
}
|
|
@@ -2141,7 +2209,8 @@ var ComplianceClient = class {
|
|
|
2141
2209
|
is_blocked: risk.is_blocked,
|
|
2142
2210
|
risk_level: risk.level,
|
|
2143
2211
|
risk_score: risk.score,
|
|
2144
|
-
risk_reasons:
|
|
2212
|
+
risk_reasons: [],
|
|
2213
|
+
risk_reasons_structured: risk.is_blocked && risk.block_reasons_structured ? risk.block_reasons_structured : [],
|
|
2145
2214
|
jurisdiction: ct.jurisdiction,
|
|
2146
2215
|
geofence_evaluation: ct.geofence_evaluation,
|
|
2147
2216
|
record_id: ct.record_id ?? "",
|
|
@@ -2156,35 +2225,35 @@ var ComplianceClient = class {
|
|
|
2156
2225
|
);
|
|
2157
2226
|
}
|
|
2158
2227
|
let riskScore = 0;
|
|
2159
|
-
const
|
|
2228
|
+
const structuredFactors = [];
|
|
2160
2229
|
const normalizedAmount = this.normalizeToUSD(amount, currency);
|
|
2161
2230
|
if (normalizedAmount > 1e4) {
|
|
2162
2231
|
riskScore += 30;
|
|
2163
|
-
|
|
2232
|
+
structuredFactors.push(sdkReasons.transactionHighAmount(normalizedAmount, currency, 1e4));
|
|
2164
2233
|
} else if (normalizedAmount > 5e3) {
|
|
2165
2234
|
riskScore += 15;
|
|
2166
|
-
|
|
2235
|
+
structuredFactors.push(sdkReasons.transactionElevatedAmount(normalizedAmount, currency, 5e3));
|
|
2167
2236
|
}
|
|
2168
2237
|
riskScore += geoVerification.risk_score * 0.4;
|
|
2169
2238
|
if (geoVerification.risk_level === "high" || geoVerification.risk_level === "critical") {
|
|
2170
|
-
|
|
2239
|
+
structuredFactors.push(sdkReasons.riskHighLocation());
|
|
2171
2240
|
}
|
|
2172
2241
|
riskScore += profile.risk_score * 0.3;
|
|
2173
2242
|
if (profile.risk_category === "high" || profile.risk_category === "critical") {
|
|
2174
|
-
|
|
2243
|
+
structuredFactors.push(sdkReasons.riskHighCustomer());
|
|
2175
2244
|
}
|
|
2176
2245
|
if (geoVerification.location.is_vpn || geoVerification.location.is_proxy) {
|
|
2177
2246
|
riskScore += 20;
|
|
2178
|
-
|
|
2247
|
+
structuredFactors.push(sdkReasons.anonymizationDetected());
|
|
2179
2248
|
}
|
|
2180
2249
|
if (profile.customer_status === "suspended") {
|
|
2181
2250
|
riskScore += 50;
|
|
2182
|
-
|
|
2251
|
+
structuredFactors.push(sdkReasons.accountSuspended());
|
|
2183
2252
|
}
|
|
2184
2253
|
if (cipherTextResult) {
|
|
2185
2254
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
2186
2255
|
riskScore += 20;
|
|
2187
|
-
|
|
2256
|
+
structuredFactors.push(sdkReasons.gpsIPMismatch());
|
|
2188
2257
|
}
|
|
2189
2258
|
if (cipherTextResult.risk?.score) {
|
|
2190
2259
|
riskScore += cipherTextResult.risk.score * 0.2;
|
|
@@ -2193,7 +2262,8 @@ var ComplianceClient = class {
|
|
|
2193
2262
|
return {
|
|
2194
2263
|
score: Math.min(riskScore, 100),
|
|
2195
2264
|
level: this.getRiskLevel(riskScore),
|
|
2196
|
-
factors,
|
|
2265
|
+
factors: structuredFactors.map((r) => r.message),
|
|
2266
|
+
factorsStructured: structuredFactors,
|
|
2197
2267
|
allowed: riskScore < 70,
|
|
2198
2268
|
requiresManualReview: riskScore >= 60 && riskScore < 70
|
|
2199
2269
|
};
|
|
@@ -2218,52 +2288,62 @@ var ComplianceClient = class {
|
|
|
2218
2288
|
getBlockReasons(geoVerification, profile, cipherTextResult) {
|
|
2219
2289
|
const reasons = [];
|
|
2220
2290
|
if (geoVerification.is_blocked) {
|
|
2221
|
-
reasons.push(...geoVerification.
|
|
2291
|
+
reasons.push(...geoVerification.risk_reasons_structured ?? []);
|
|
2222
2292
|
}
|
|
2223
2293
|
if (profile) {
|
|
2224
2294
|
if (profile.customer_status === "suspended") {
|
|
2225
|
-
reasons.push(
|
|
2295
|
+
reasons.push(sdkReasons.accountSuspended());
|
|
2226
2296
|
}
|
|
2227
2297
|
if (profile.has_sanctions) {
|
|
2228
|
-
reasons.push(
|
|
2298
|
+
reasons.push(sdkReasons.sanctionsMatch());
|
|
2229
2299
|
}
|
|
2230
2300
|
}
|
|
2231
2301
|
if (cipherTextResult) {
|
|
2232
2302
|
if (!cipherTextResult.valid) {
|
|
2233
|
-
reasons.push(
|
|
2303
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2234
2304
|
}
|
|
2235
2305
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2236
|
-
reasons.push(...cipherTextResult.risk.
|
|
2306
|
+
reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2237
2307
|
}
|
|
2238
2308
|
}
|
|
2239
2309
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2240
|
-
reasons.push(
|
|
2310
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2241
2311
|
}
|
|
2242
|
-
|
|
2312
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2313
|
+
return reasons.filter((r) => {
|
|
2314
|
+
if (seen.has(r.code)) return false;
|
|
2315
|
+
seen.add(r.code);
|
|
2316
|
+
return true;
|
|
2317
|
+
});
|
|
2243
2318
|
}
|
|
2244
2319
|
getTransactionBlockReasons(geoVerification, transactionRisk, jurisdictionAllowed, cipherTextResult) {
|
|
2245
2320
|
const reasons = [];
|
|
2246
2321
|
if (!geoVerification.is_compliant) {
|
|
2247
|
-
reasons.push(
|
|
2322
|
+
reasons.push(sdkReasons.jurisdictionNonCompliant());
|
|
2248
2323
|
}
|
|
2249
2324
|
if (!jurisdictionAllowed) {
|
|
2250
|
-
reasons.push(
|
|
2325
|
+
reasons.push(sdkReasons.transactionJurisdictionLimit());
|
|
2251
2326
|
}
|
|
2252
2327
|
if (!transactionRisk.allowed) {
|
|
2253
|
-
reasons.push(...transactionRisk.
|
|
2328
|
+
reasons.push(...transactionRisk.factorsStructured ?? []);
|
|
2254
2329
|
}
|
|
2255
2330
|
if (cipherTextResult) {
|
|
2256
2331
|
if (!cipherTextResult.valid) {
|
|
2257
|
-
reasons.push(
|
|
2332
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2258
2333
|
}
|
|
2259
2334
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2260
|
-
reasons.push(...cipherTextResult.risk.
|
|
2335
|
+
reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2261
2336
|
}
|
|
2262
2337
|
}
|
|
2263
2338
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2264
|
-
reasons.push(
|
|
2339
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2265
2340
|
}
|
|
2266
|
-
|
|
2341
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2342
|
+
return reasons.filter((r) => {
|
|
2343
|
+
if (seen.has(r.code)) return false;
|
|
2344
|
+
seen.add(r.code);
|
|
2345
|
+
return true;
|
|
2346
|
+
});
|
|
2267
2347
|
}
|
|
2268
2348
|
// ============================================================================
|
|
2269
2349
|
// Location Request Methods
|
|
@@ -2481,6 +2561,6 @@ var ComplianceClient = class {
|
|
|
2481
2561
|
}
|
|
2482
2562
|
};
|
|
2483
2563
|
|
|
2484
|
-
export { ComplianceClient, DEFAULT_CURRENCY_RATES };
|
|
2564
|
+
export { ComplianceClient, DEFAULT_CURRENCY_RATES, sdkReasons };
|
|
2485
2565
|
//# sourceMappingURL=index.mjs.map
|
|
2486
2566
|
//# sourceMappingURL=index.mjs.map
|