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
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,62 @@ 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.
|
|
1715
|
+
blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
1657
1716
|
}
|
|
1658
1717
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
1659
|
-
blockReasons.push(
|
|
1718
|
+
blockReasons.push(sdkReasons.gpsIPMismatch());
|
|
1660
1719
|
}
|
|
1661
1720
|
}
|
|
1662
1721
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
1663
|
-
blockReasons.push(
|
|
1722
|
+
blockReasons.push(sdkReasons.gpsRequired());
|
|
1664
1723
|
}
|
|
1665
|
-
|
|
1724
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1725
|
+
return blockReasons.filter((r) => {
|
|
1726
|
+
if (seen.has(r.code)) return false;
|
|
1727
|
+
seen.add(r.code);
|
|
1728
|
+
return true;
|
|
1729
|
+
});
|
|
1666
1730
|
}
|
|
1667
1731
|
/**
|
|
1668
1732
|
* Validate registration request has all required fields
|
|
@@ -1833,9 +1897,7 @@ var ComplianceClient = class {
|
|
|
1833
1897
|
geolocation: geoVerification,
|
|
1834
1898
|
profile: null,
|
|
1835
1899
|
requiresStepUp: false,
|
|
1836
|
-
|
|
1837
|
-
processingTime: Date.now() - startTime,
|
|
1838
|
-
cipherTextValidation: cipherTextResult
|
|
1900
|
+
...this.buildReasonTail(loginBlockReasons, startTime, cipherTextResult)
|
|
1839
1901
|
};
|
|
1840
1902
|
}
|
|
1841
1903
|
let profile;
|
|
@@ -1855,14 +1917,13 @@ var ComplianceClient = class {
|
|
|
1855
1917
|
profile = await this.createProfileFromGeo(request.customerId, geoVerification);
|
|
1856
1918
|
}
|
|
1857
1919
|
const requiresStepUp = geoVerification.risk_level === "high" || geoVerification.risk_level === "critical" || cipherTextResult?.risk?.location_mismatch === true;
|
|
1920
|
+
const loginFinalStructured = isBlocked || profile.customer_status === "suspended" ? this.getBlockReasons(geoVerification, profile, cipherTextResult) : [];
|
|
1858
1921
|
return {
|
|
1859
1922
|
allowed: !isBlocked && profile.customer_status !== "suspended",
|
|
1860
1923
|
geolocation: geoVerification,
|
|
1861
1924
|
profile,
|
|
1862
1925
|
requiresStepUp,
|
|
1863
|
-
|
|
1864
|
-
processingTime: Date.now() - startTime,
|
|
1865
|
-
cipherTextValidation: cipherTextResult
|
|
1926
|
+
...this.buildReasonTail(loginFinalStructured, startTime, cipherTextResult)
|
|
1866
1927
|
};
|
|
1867
1928
|
} catch (error) {
|
|
1868
1929
|
throw new ComplianceError("Login verification failed", error instanceof Error ? error.message : void 0);
|
|
@@ -1944,20 +2005,19 @@ var ComplianceClient = class {
|
|
|
1944
2005
|
}
|
|
1945
2006
|
const geoBlocked = !geoVerification.is_compliant || geoVerification.is_blocked || !!cipherTextResult?.risk?.is_blocked || cipherTextResult?.valid === false || geoVerification.gps_required && !cipherTextResult;
|
|
1946
2007
|
if (geoBlocked && !profileResult) {
|
|
2008
|
+
const earlyStructured = this.getTransactionBlockReasons(
|
|
2009
|
+
geoVerification,
|
|
2010
|
+
{ score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
|
|
2011
|
+
true,
|
|
2012
|
+
cipherTextResult
|
|
2013
|
+
);
|
|
1947
2014
|
return {
|
|
1948
2015
|
allowed: false,
|
|
1949
2016
|
geolocation: geoVerification,
|
|
1950
2017
|
profile: null,
|
|
1951
2018
|
transactionRisk: { score: 0, level: "low", factors: [], allowed: false, requiresManualReview: false },
|
|
1952
2019
|
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
|
|
2020
|
+
...this.buildReasonTail(earlyStructured, startTime, cipherTextResult)
|
|
1961
2021
|
};
|
|
1962
2022
|
}
|
|
1963
2023
|
if (!profileResult) {
|
|
@@ -1977,20 +2037,19 @@ var ComplianceClient = class {
|
|
|
1977
2037
|
geoVerification.jurisdiction
|
|
1978
2038
|
);
|
|
1979
2039
|
const isAllowed = !geoBlocked && jurisdictionAllowed && transactionRisk.allowed && profile.customer_status !== "suspended";
|
|
2040
|
+
const txStructured = this.getTransactionBlockReasons(
|
|
2041
|
+
geoVerification,
|
|
2042
|
+
transactionRisk,
|
|
2043
|
+
jurisdictionAllowed,
|
|
2044
|
+
cipherTextResult
|
|
2045
|
+
);
|
|
1980
2046
|
return {
|
|
1981
2047
|
allowed: isAllowed,
|
|
1982
2048
|
geolocation: geoVerification,
|
|
1983
2049
|
profile,
|
|
1984
2050
|
transactionRisk,
|
|
1985
2051
|
requiresApproval: transactionRisk.requiresManualReview,
|
|
1986
|
-
|
|
1987
|
-
geoVerification,
|
|
1988
|
-
transactionRisk,
|
|
1989
|
-
jurisdictionAllowed,
|
|
1990
|
-
cipherTextResult
|
|
1991
|
-
),
|
|
1992
|
-
processingTime: Date.now() - startTime,
|
|
1993
|
-
cipherTextValidation: cipherTextResult
|
|
2052
|
+
...this.buildReasonTail(txStructured, startTime, cipherTextResult)
|
|
1994
2053
|
};
|
|
1995
2054
|
} catch (error) {
|
|
1996
2055
|
throw new ComplianceError("Transaction verification failed", error instanceof Error ? error.message : void 0);
|
|
@@ -2043,30 +2102,42 @@ var ComplianceClient = class {
|
|
|
2043
2102
|
}
|
|
2044
2103
|
}
|
|
2045
2104
|
const cipherTextBlocked = cipherTextResult ? !cipherTextResult.valid || cipherTextResult.risk?.is_blocked === true : false;
|
|
2046
|
-
const
|
|
2105
|
+
const structuredEventReasons = [...geoVerification.risk_reasons_structured ?? []];
|
|
2047
2106
|
if (cipherTextResult) {
|
|
2048
2107
|
if (!cipherTextResult.valid) {
|
|
2049
|
-
|
|
2108
|
+
structuredEventReasons.push(sdkReasons.ciphertextInvalid());
|
|
2050
2109
|
}
|
|
2051
2110
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2052
|
-
|
|
2111
|
+
structuredEventReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2053
2112
|
}
|
|
2054
2113
|
}
|
|
2055
2114
|
const gpsBlocked = geoVerification.gps_required && !cipherTextResult;
|
|
2056
2115
|
if (gpsBlocked) {
|
|
2057
|
-
|
|
2116
|
+
structuredEventReasons.push(sdkReasons.gpsRequired());
|
|
2058
2117
|
}
|
|
2118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2119
|
+
const dedupedStructured = structuredEventReasons.filter((r) => {
|
|
2120
|
+
if (seen.has(r.code)) return false;
|
|
2121
|
+
seen.add(r.code);
|
|
2122
|
+
return true;
|
|
2123
|
+
});
|
|
2059
2124
|
return {
|
|
2060
2125
|
allowed: geoVerification.is_compliant && !geoVerification.is_blocked && !cipherTextBlocked && !gpsBlocked,
|
|
2061
2126
|
geolocation: geoVerification,
|
|
2062
|
-
|
|
2063
|
-
processingTime: Date.now() - startTime,
|
|
2064
|
-
cipherTextValidation: cipherTextResult
|
|
2127
|
+
...this.buildReasonTail(dedupedStructured, startTime, cipherTextResult)
|
|
2065
2128
|
};
|
|
2066
2129
|
}
|
|
2067
2130
|
// ============================================================================
|
|
2068
2131
|
// Helper Methods
|
|
2069
2132
|
// ============================================================================
|
|
2133
|
+
buildReasonTail(structured, startTime, cipherTextResult) {
|
|
2134
|
+
return {
|
|
2135
|
+
blockReasons: structured.map((r) => r.message),
|
|
2136
|
+
blockReasonsStructured: structured,
|
|
2137
|
+
processingTime: Date.now() - startTime,
|
|
2138
|
+
cipherTextValidation: cipherTextResult
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2070
2141
|
shouldUpdateProfile(profile, newCity) {
|
|
2071
2142
|
return !profile.location || !profile.location.includes(newCity);
|
|
2072
2143
|
}
|
|
@@ -2143,7 +2214,8 @@ var ComplianceClient = class {
|
|
|
2143
2214
|
is_blocked: risk.is_blocked,
|
|
2144
2215
|
risk_level: risk.level,
|
|
2145
2216
|
risk_score: risk.score,
|
|
2146
|
-
risk_reasons:
|
|
2217
|
+
risk_reasons: [],
|
|
2218
|
+
risk_reasons_structured: risk.is_blocked && risk.block_reasons_structured ? risk.block_reasons_structured : [],
|
|
2147
2219
|
jurisdiction: ct.jurisdiction,
|
|
2148
2220
|
geofence_evaluation: ct.geofence_evaluation,
|
|
2149
2221
|
record_id: ct.record_id ?? "",
|
|
@@ -2158,35 +2230,35 @@ var ComplianceClient = class {
|
|
|
2158
2230
|
);
|
|
2159
2231
|
}
|
|
2160
2232
|
let riskScore = 0;
|
|
2161
|
-
const
|
|
2233
|
+
const structuredFactors = [];
|
|
2162
2234
|
const normalizedAmount = this.normalizeToUSD(amount, currency);
|
|
2163
2235
|
if (normalizedAmount > 1e4) {
|
|
2164
2236
|
riskScore += 30;
|
|
2165
|
-
|
|
2237
|
+
structuredFactors.push(sdkReasons.transactionHighAmount(normalizedAmount, currency, 1e4));
|
|
2166
2238
|
} else if (normalizedAmount > 5e3) {
|
|
2167
2239
|
riskScore += 15;
|
|
2168
|
-
|
|
2240
|
+
structuredFactors.push(sdkReasons.transactionElevatedAmount(normalizedAmount, currency, 5e3));
|
|
2169
2241
|
}
|
|
2170
2242
|
riskScore += geoVerification.risk_score * 0.4;
|
|
2171
2243
|
if (geoVerification.risk_level === "high" || geoVerification.risk_level === "critical") {
|
|
2172
|
-
|
|
2244
|
+
structuredFactors.push(sdkReasons.riskHighLocation());
|
|
2173
2245
|
}
|
|
2174
2246
|
riskScore += profile.risk_score * 0.3;
|
|
2175
2247
|
if (profile.risk_category === "high" || profile.risk_category === "critical") {
|
|
2176
|
-
|
|
2248
|
+
structuredFactors.push(sdkReasons.riskHighCustomer());
|
|
2177
2249
|
}
|
|
2178
2250
|
if (geoVerification.location.is_vpn || geoVerification.location.is_proxy) {
|
|
2179
2251
|
riskScore += 20;
|
|
2180
|
-
|
|
2252
|
+
structuredFactors.push(sdkReasons.anonymizationDetected());
|
|
2181
2253
|
}
|
|
2182
2254
|
if (profile.customer_status === "suspended") {
|
|
2183
2255
|
riskScore += 50;
|
|
2184
|
-
|
|
2256
|
+
structuredFactors.push(sdkReasons.accountSuspended());
|
|
2185
2257
|
}
|
|
2186
2258
|
if (cipherTextResult) {
|
|
2187
2259
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
2188
2260
|
riskScore += 20;
|
|
2189
|
-
|
|
2261
|
+
structuredFactors.push(sdkReasons.gpsIPMismatch());
|
|
2190
2262
|
}
|
|
2191
2263
|
if (cipherTextResult.risk?.score) {
|
|
2192
2264
|
riskScore += cipherTextResult.risk.score * 0.2;
|
|
@@ -2195,7 +2267,8 @@ var ComplianceClient = class {
|
|
|
2195
2267
|
return {
|
|
2196
2268
|
score: Math.min(riskScore, 100),
|
|
2197
2269
|
level: this.getRiskLevel(riskScore),
|
|
2198
|
-
factors,
|
|
2270
|
+
factors: structuredFactors.map((r) => r.message),
|
|
2271
|
+
factorsStructured: structuredFactors,
|
|
2199
2272
|
allowed: riskScore < 70,
|
|
2200
2273
|
requiresManualReview: riskScore >= 60 && riskScore < 70
|
|
2201
2274
|
};
|
|
@@ -2220,52 +2293,62 @@ var ComplianceClient = class {
|
|
|
2220
2293
|
getBlockReasons(geoVerification, profile, cipherTextResult) {
|
|
2221
2294
|
const reasons = [];
|
|
2222
2295
|
if (geoVerification.is_blocked) {
|
|
2223
|
-
reasons.push(...geoVerification.
|
|
2296
|
+
reasons.push(...geoVerification.risk_reasons_structured ?? []);
|
|
2224
2297
|
}
|
|
2225
2298
|
if (profile) {
|
|
2226
2299
|
if (profile.customer_status === "suspended") {
|
|
2227
|
-
reasons.push(
|
|
2300
|
+
reasons.push(sdkReasons.accountSuspended());
|
|
2228
2301
|
}
|
|
2229
2302
|
if (profile.has_sanctions) {
|
|
2230
|
-
reasons.push(
|
|
2303
|
+
reasons.push(sdkReasons.sanctionsMatch());
|
|
2231
2304
|
}
|
|
2232
2305
|
}
|
|
2233
2306
|
if (cipherTextResult) {
|
|
2234
2307
|
if (!cipherTextResult.valid) {
|
|
2235
|
-
reasons.push(
|
|
2308
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2236
2309
|
}
|
|
2237
2310
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2238
|
-
reasons.push(...cipherTextResult.risk.
|
|
2311
|
+
reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2239
2312
|
}
|
|
2240
2313
|
}
|
|
2241
2314
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2242
|
-
reasons.push(
|
|
2315
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2243
2316
|
}
|
|
2244
|
-
|
|
2317
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2318
|
+
return reasons.filter((r) => {
|
|
2319
|
+
if (seen.has(r.code)) return false;
|
|
2320
|
+
seen.add(r.code);
|
|
2321
|
+
return true;
|
|
2322
|
+
});
|
|
2245
2323
|
}
|
|
2246
2324
|
getTransactionBlockReasons(geoVerification, transactionRisk, jurisdictionAllowed, cipherTextResult) {
|
|
2247
2325
|
const reasons = [];
|
|
2248
2326
|
if (!geoVerification.is_compliant) {
|
|
2249
|
-
reasons.push(
|
|
2327
|
+
reasons.push(sdkReasons.jurisdictionNonCompliant());
|
|
2250
2328
|
}
|
|
2251
2329
|
if (!jurisdictionAllowed) {
|
|
2252
|
-
reasons.push(
|
|
2330
|
+
reasons.push(sdkReasons.transactionJurisdictionLimit());
|
|
2253
2331
|
}
|
|
2254
2332
|
if (!transactionRisk.allowed) {
|
|
2255
|
-
reasons.push(...transactionRisk.
|
|
2333
|
+
reasons.push(...transactionRisk.factorsStructured ?? []);
|
|
2256
2334
|
}
|
|
2257
2335
|
if (cipherTextResult) {
|
|
2258
2336
|
if (!cipherTextResult.valid) {
|
|
2259
|
-
reasons.push(
|
|
2337
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2260
2338
|
}
|
|
2261
2339
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2262
|
-
reasons.push(...cipherTextResult.risk.
|
|
2340
|
+
reasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
2263
2341
|
}
|
|
2264
2342
|
}
|
|
2265
2343
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2266
|
-
reasons.push(
|
|
2344
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2267
2345
|
}
|
|
2268
|
-
|
|
2346
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2347
|
+
return reasons.filter((r) => {
|
|
2348
|
+
if (seen.has(r.code)) return false;
|
|
2349
|
+
seen.add(r.code);
|
|
2350
|
+
return true;
|
|
2351
|
+
});
|
|
2269
2352
|
}
|
|
2270
2353
|
// ============================================================================
|
|
2271
2354
|
// Location Request Methods
|
|
@@ -2485,5 +2568,6 @@ var ComplianceClient = class {
|
|
|
2485
2568
|
|
|
2486
2569
|
exports.ComplianceClient = ComplianceClient;
|
|
2487
2570
|
exports.DEFAULT_CURRENCY_RATES = DEFAULT_CURRENCY_RATES;
|
|
2571
|
+
exports.sdkReasons = sdkReasons;
|
|
2488
2572
|
//# sourceMappingURL=index.js.map
|
|
2489
2573
|
//# sourceMappingURL=index.js.map
|