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