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.
- package/README.md +14 -4
- 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-C3DCmGe9.d.ts +436 -0
- package/dist/{client-C_A7QLcB.d.ts → client-DMIRx7Tu.d.mts} +5 -3
- package/dist/{client-BlCxjbY2.d.mts → client-DoMSYMMR.d.ts} +5 -3
- package/dist/client-ZNdnpWe7.d.mts +436 -0
- package/dist/compliance/index.d.mts +25 -429
- package/dist/compliance/index.d.ts +25 -429
- package/dist/compliance/index.js +187 -103
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +187 -104
- 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 +294 -292
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +293 -291
- 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 +78 -23
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +78 -24
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +269 -45
- package/dist/kyc/index.d.ts +269 -45
- package/dist/kyc/index.js +78 -23
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +78 -24
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +42 -7
- package/dist/react.d.ts +42 -7
- package/dist/react.js +663 -277
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +663 -277
- 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 +6 -41
- package/dist/tax/index.d.ts +6 -41
- package/dist/tax/index.js +1 -36
- package/dist/tax/index.js.map +1 -1
- package/dist/tax/index.mjs +1 -36
- 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,62 @@ 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.
|
|
1713
|
+
blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
1655
1714
|
}
|
|
1656
1715
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
1657
|
-
blockReasons.push(
|
|
1716
|
+
blockReasons.push(sdkReasons.gpsIPMismatch());
|
|
1658
1717
|
}
|
|
1659
1718
|
}
|
|
1660
1719
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
1661
|
-
blockReasons.push(
|
|
1720
|
+
blockReasons.push(sdkReasons.gpsRequired());
|
|
1662
1721
|
}
|
|
1663
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2103
|
+
const structuredEventReasons = [...geoVerification.risk_reasons_structured ?? []];
|
|
2045
2104
|
if (cipherTextResult) {
|
|
2046
2105
|
if (!cipherTextResult.valid) {
|
|
2047
|
-
|
|
2106
|
+
structuredEventReasons.push(sdkReasons.ciphertextInvalid());
|
|
2048
2107
|
}
|
|
2049
2108
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2050
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
2231
|
+
const structuredFactors = [];
|
|
2160
2232
|
const normalizedAmount = this.normalizeToUSD(amount, currency);
|
|
2161
2233
|
if (normalizedAmount > 1e4) {
|
|
2162
2234
|
riskScore += 30;
|
|
2163
|
-
|
|
2235
|
+
structuredFactors.push(sdkReasons.transactionHighAmount(normalizedAmount, currency, 1e4));
|
|
2164
2236
|
} else if (normalizedAmount > 5e3) {
|
|
2165
2237
|
riskScore += 15;
|
|
2166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2246
|
+
structuredFactors.push(sdkReasons.riskHighCustomer());
|
|
2175
2247
|
}
|
|
2176
2248
|
if (geoVerification.location.is_vpn || geoVerification.location.is_proxy) {
|
|
2177
2249
|
riskScore += 20;
|
|
2178
|
-
|
|
2250
|
+
structuredFactors.push(sdkReasons.anonymizationDetected());
|
|
2179
2251
|
}
|
|
2180
2252
|
if (profile.customer_status === "suspended") {
|
|
2181
2253
|
riskScore += 50;
|
|
2182
|
-
|
|
2254
|
+
structuredFactors.push(sdkReasons.accountSuspended());
|
|
2183
2255
|
}
|
|
2184
2256
|
if (cipherTextResult) {
|
|
2185
2257
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
2186
2258
|
riskScore += 20;
|
|
2187
|
-
|
|
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.
|
|
2294
|
+
reasons.push(...geoVerification.risk_reasons_structured ?? []);
|
|
2222
2295
|
}
|
|
2223
2296
|
if (profile) {
|
|
2224
2297
|
if (profile.customer_status === "suspended") {
|
|
2225
|
-
reasons.push(
|
|
2298
|
+
reasons.push(sdkReasons.accountSuspended());
|
|
2226
2299
|
}
|
|
2227
2300
|
if (profile.has_sanctions) {
|
|
2228
|
-
reasons.push(
|
|
2301
|
+
reasons.push(sdkReasons.sanctionsMatch());
|
|
2229
2302
|
}
|
|
2230
2303
|
}
|
|
2231
2304
|
if (cipherTextResult) {
|
|
2232
2305
|
if (!cipherTextResult.valid) {
|
|
2233
|
-
reasons.push(
|
|
2306
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2234
2307
|
}
|
|
2235
2308
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2236
|
-
reasons.push(...cipherTextResult.risk.
|
|
2309
|
+
reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2237
2310
|
}
|
|
2238
2311
|
}
|
|
2239
2312
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2240
|
-
reasons.push(
|
|
2313
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2241
2314
|
}
|
|
2242
|
-
|
|
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(
|
|
2325
|
+
reasons.push(sdkReasons.jurisdictionNonCompliant());
|
|
2248
2326
|
}
|
|
2249
2327
|
if (!jurisdictionAllowed) {
|
|
2250
|
-
reasons.push(
|
|
2328
|
+
reasons.push(sdkReasons.transactionJurisdictionLimit());
|
|
2251
2329
|
}
|
|
2252
2330
|
if (!transactionRisk.allowed) {
|
|
2253
|
-
reasons.push(...transactionRisk.
|
|
2331
|
+
reasons.push(...transactionRisk.factorsStructured ?? []);
|
|
2254
2332
|
}
|
|
2255
2333
|
if (cipherTextResult) {
|
|
2256
2334
|
if (!cipherTextResult.valid) {
|
|
2257
|
-
reasons.push(
|
|
2335
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2258
2336
|
}
|
|
2259
2337
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2260
|
-
reasons.push(...cipherTextResult.risk.
|
|
2338
|
+
reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2261
2339
|
}
|
|
2262
2340
|
}
|
|
2263
2341
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2264
|
-
reasons.push(
|
|
2342
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2265
2343
|
}
|
|
2266
|
-
|
|
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
|