vesant-sdk 1.6.6 → 2.0.0-dev.40d1c39
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-BJ87_Vv5.d.ts +430 -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-Bvp-f05-.d.ts} +7 -7
- package/dist/{client-C_A7QLcB.d.ts → client-CIEa7xYG.d.mts} +7 -7
- package/dist/client-IAOGCBfm.d.mts +430 -0
- package/dist/compliance/index.d.mts +25 -429
- package/dist/compliance/index.d.ts +25 -429
- package/dist/compliance/index.js +137 -58
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +137 -59
- 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 +6 -24
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +6 -24
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index.d.mts +14 -71
- package/dist/index.d.ts +14 -71
- package/dist/index.js +190 -237
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +189 -236
- 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 +24 -13
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +24 -14
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +20 -27
- package/dist/kyc/index.d.ts +20 -27
- package/dist/kyc/index.js +24 -13
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +24 -14
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +6 -6
- package/dist/react.d.ts +6 -6
- package/dist/react.js +18 -5
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +18 -5
- 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-X5Md_dD_.d.ts → types-2utj53GK.d.ts} +2 -2
- package/dist/{types-1RzYeSal.d.mts → types-C4Zx0d_u.d.mts} +2 -2
- package/dist/{types-B4Ezqo7V.d.mts → types-QUCWam16.d.mts} +7 -1
- package/dist/{types-B4Ezqo7V.d.ts → types-QUCWam16.d.ts} +7 -1
- package/dist/webhooks/index.d.mts +181 -2
- package/dist/webhooks/index.d.ts +181 -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/index.js
CHANGED
|
@@ -242,7 +242,7 @@ var noopLogger = {
|
|
|
242
242
|
};
|
|
243
243
|
|
|
244
244
|
// src/core/version.ts
|
|
245
|
-
var SDK_VERSION = "
|
|
245
|
+
var SDK_VERSION = "2.0.0";
|
|
246
246
|
|
|
247
247
|
// src/shared/browser-utils.ts
|
|
248
248
|
function generateUUID() {
|
|
@@ -652,8 +652,9 @@ async function computeHmacSha256(message, secret) {
|
|
|
652
652
|
const { createHmac } = await import('crypto');
|
|
653
653
|
return createHmac("sha256", secret).update(message).digest("hex");
|
|
654
654
|
} catch {
|
|
655
|
-
throw new
|
|
656
|
-
"No crypto implementation available. Requires Web Crypto API or Node.js crypto module."
|
|
655
|
+
throw new VesantError(
|
|
656
|
+
"No crypto implementation available. Requires Web Crypto API or Node.js crypto module.",
|
|
657
|
+
"CRYPTO_UNAVAILABLE"
|
|
657
658
|
);
|
|
658
659
|
}
|
|
659
660
|
}
|
|
@@ -787,7 +788,7 @@ function encodePayload(payload) {
|
|
|
787
788
|
} else if (typeof Buffer !== "undefined") {
|
|
788
789
|
return Buffer.from(json, "utf-8").toString("base64");
|
|
789
790
|
}
|
|
790
|
-
throw new
|
|
791
|
+
throw new VesantError("No base64 encoding method available", "BASE64_UNAVAILABLE");
|
|
791
792
|
}
|
|
792
793
|
async function generateCipherText(options, config) {
|
|
793
794
|
const warnings = [];
|
|
@@ -812,8 +813,9 @@ async function generateCipherText(options, config) {
|
|
|
812
813
|
if (location) {
|
|
813
814
|
locationData = location;
|
|
814
815
|
} else if (gpsRequiredByConfig) {
|
|
815
|
-
throw new
|
|
816
|
-
`GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied
|
|
816
|
+
throw new VesantError(
|
|
817
|
+
`GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
|
|
818
|
+
"GPS_REQUIRED"
|
|
817
819
|
);
|
|
818
820
|
} else {
|
|
819
821
|
warnings.push("GPS location not available or permission denied");
|
|
@@ -932,10 +934,9 @@ var GeolocationClient = class extends BaseClient {
|
|
|
932
934
|
if (!request.ip_address?.trim()) {
|
|
933
935
|
throw new ValidationError("ip_address is required and must be a non-empty string", ["ip_address"]);
|
|
934
936
|
}
|
|
935
|
-
const enrichedRequest = request.device_fingerprint ? request : { ...request, device_fingerprint: collectDeviceFingerprint() };
|
|
936
937
|
return this.requestWithRetry("/api/v1/geo/verify", {
|
|
937
938
|
method: "POST",
|
|
938
|
-
body: JSON.stringify(
|
|
939
|
+
body: JSON.stringify(request)
|
|
939
940
|
}, void 0, void 0, requestOptions);
|
|
940
941
|
}
|
|
941
942
|
/**
|
|
@@ -1339,24 +1340,6 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1339
1340
|
// Utility Methods (inherited from BaseClient: healthCheck, updateConfig, getConfig, buildQueryString)
|
|
1340
1341
|
// ============================================================================
|
|
1341
1342
|
};
|
|
1342
|
-
function collectDeviceFingerprint() {
|
|
1343
|
-
const deviceId = generateDeviceId();
|
|
1344
|
-
const browserInfo = getBrowserInfo();
|
|
1345
|
-
const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "server";
|
|
1346
|
-
const platform = typeof navigator !== "undefined" ? navigator.platform || "unknown" : "server";
|
|
1347
|
-
return {
|
|
1348
|
-
device_id: deviceId,
|
|
1349
|
-
user_agent: userAgent,
|
|
1350
|
-
platform,
|
|
1351
|
-
browser: browserInfo.browser,
|
|
1352
|
-
browser_version: browserInfo.browser_version,
|
|
1353
|
-
os: browserInfo.os,
|
|
1354
|
-
os_version: browserInfo.os_version,
|
|
1355
|
-
screen_resolution: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0,
|
|
1356
|
-
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
1357
|
-
timezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
1343
|
|
|
1361
1344
|
// src/risk-profile/client.ts
|
|
1362
1345
|
var RiskProfileClient = class extends BaseClient {
|
|
@@ -1473,6 +1456,81 @@ var RiskProfileClient = class extends BaseClient {
|
|
|
1473
1456
|
}
|
|
1474
1457
|
};
|
|
1475
1458
|
|
|
1459
|
+
// src/compliance/block-reasons.ts
|
|
1460
|
+
var sdkReasons = {
|
|
1461
|
+
// Jurisdiction
|
|
1462
|
+
jurisdictionBlocked: (country, countryISO) => ({
|
|
1463
|
+
code: "JURISDICTION_BLOCKED",
|
|
1464
|
+
message: "Access from a blocked jurisdiction",
|
|
1465
|
+
metadata: { country, country_iso: countryISO }
|
|
1466
|
+
}),
|
|
1467
|
+
jurisdictionNonCompliant: () => ({
|
|
1468
|
+
code: "JURISDICTION_NON_COMPLIANT",
|
|
1469
|
+
message: "Location does not meet compliance requirements"
|
|
1470
|
+
}),
|
|
1471
|
+
jurisdictionRegistrationDenied: () => ({
|
|
1472
|
+
code: "JURISDICTION_REGISTRATION_DENIED",
|
|
1473
|
+
message: "Registration is not permitted in this jurisdiction"
|
|
1474
|
+
}),
|
|
1475
|
+
jurisdictionRestricted: () => ({
|
|
1476
|
+
code: "JURISDICTION_RESTRICTED",
|
|
1477
|
+
message: "Access from a restricted jurisdiction"
|
|
1478
|
+
}),
|
|
1479
|
+
// Risk
|
|
1480
|
+
riskCriticalLevel: () => ({
|
|
1481
|
+
code: "RISK_CRITICAL_LEVEL",
|
|
1482
|
+
message: "Risk assessment reached critical threshold"
|
|
1483
|
+
}),
|
|
1484
|
+
accountSuspended: () => ({
|
|
1485
|
+
code: "RISK_ACCOUNT_SUSPENDED",
|
|
1486
|
+
message: "Customer account is suspended"
|
|
1487
|
+
}),
|
|
1488
|
+
sanctionsMatch: () => ({
|
|
1489
|
+
code: "RISK_SANCTIONS_MATCH",
|
|
1490
|
+
message: "Customer profile matched against sanctions list"
|
|
1491
|
+
}),
|
|
1492
|
+
riskHighLocation: () => ({
|
|
1493
|
+
code: "RISK_HIGH_LOCATION",
|
|
1494
|
+
message: "High-risk geographic location"
|
|
1495
|
+
}),
|
|
1496
|
+
riskHighCustomer: () => ({
|
|
1497
|
+
code: "RISK_HIGH_CUSTOMER",
|
|
1498
|
+
message: "Customer flagged as high risk"
|
|
1499
|
+
}),
|
|
1500
|
+
// Device
|
|
1501
|
+
ciphertextInvalid: () => ({
|
|
1502
|
+
code: "DEVICE_CIPHERTEXT_INVALID",
|
|
1503
|
+
message: "Device verification payload failed validation"
|
|
1504
|
+
}),
|
|
1505
|
+
gpsIPMismatch: () => ({
|
|
1506
|
+
code: "DEVICE_GPS_IP_MISMATCH",
|
|
1507
|
+
message: "GPS location does not match IP-derived location"
|
|
1508
|
+
}),
|
|
1509
|
+
gpsRequired: () => ({
|
|
1510
|
+
code: "DEVICE_GPS_REQUIRED",
|
|
1511
|
+
message: "GPS verification is required but was not provided"
|
|
1512
|
+
}),
|
|
1513
|
+
// Transaction
|
|
1514
|
+
transactionHighAmount: (amount, currency, threshold) => ({
|
|
1515
|
+
code: "TRANSACTION_HIGH_AMOUNT",
|
|
1516
|
+
message: "Transaction amount exceeds high-value threshold",
|
|
1517
|
+
metadata: { amount, currency, threshold }
|
|
1518
|
+
}),
|
|
1519
|
+
transactionElevatedAmount: (amount, currency, threshold) => ({
|
|
1520
|
+
code: "TRANSACTION_ELEVATED_AMOUNT",
|
|
1521
|
+
message: "Transaction amount exceeds elevated-value threshold",
|
|
1522
|
+
metadata: { amount, currency, threshold }
|
|
1523
|
+
}),
|
|
1524
|
+
transactionJurisdictionLimit: () => ({
|
|
1525
|
+
code: "TRANSACTION_JURISDICTION_LIMIT",
|
|
1526
|
+
message: "Transaction amount exceeds jurisdiction limit"
|
|
1527
|
+
}),
|
|
1528
|
+
anonymizationDetected: () => ({
|
|
1529
|
+
code: "NETWORK_ANONYMIZER_DETECTED",
|
|
1530
|
+
message: "Anonymization tool detected"
|
|
1531
|
+
})
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1476
1534
|
// src/compliance/types.ts
|
|
1477
1535
|
var DEFAULT_CURRENCY_RATES = {
|
|
1478
1536
|
USD: 1,
|
|
@@ -1673,7 +1731,7 @@ var ComplianceClient = class {
|
|
|
1673
1731
|
} catch (error) {
|
|
1674
1732
|
if (this.config.debug) {
|
|
1675
1733
|
this.logger.error("Registration verification failed", {
|
|
1676
|
-
code: error instanceof
|
|
1734
|
+
code: error instanceof VesantError ? error.code : void 0,
|
|
1677
1735
|
message: error instanceof Error ? error.message : "Unknown error"
|
|
1678
1736
|
});
|
|
1679
1737
|
}
|
|
@@ -1705,23 +1763,23 @@ var ComplianceClient = class {
|
|
|
1705
1763
|
blockReasons.push(...geoVerification.risk_reasons ?? []);
|
|
1706
1764
|
}
|
|
1707
1765
|
if (!geoVerification.is_compliant) {
|
|
1708
|
-
if (!blockReasons.
|
|
1709
|
-
blockReasons.push(
|
|
1766
|
+
if (!blockReasons.some((r) => r.code === "JURISDICTION_NON_COMPLIANT")) {
|
|
1767
|
+
blockReasons.push(sdkReasons.jurisdictionNonCompliant());
|
|
1710
1768
|
}
|
|
1711
1769
|
}
|
|
1712
1770
|
const jurisdiction = geoVerification.jurisdiction;
|
|
1713
1771
|
if (jurisdiction) {
|
|
1714
1772
|
if (jurisdiction.allow_registration === false) {
|
|
1715
|
-
blockReasons.push(
|
|
1773
|
+
blockReasons.push(sdkReasons.jurisdictionRegistrationDenied());
|
|
1716
1774
|
}
|
|
1717
1775
|
if (jurisdiction.status === "blocked" || jurisdiction.status === "sanctioned") {
|
|
1718
|
-
if (!blockReasons.
|
|
1719
|
-
blockReasons.push("
|
|
1776
|
+
if (!blockReasons.some((r) => r.code === "JURISDICTION_BLOCKED")) {
|
|
1777
|
+
blockReasons.push(sdkReasons.jurisdictionBlocked(jurisdiction.country_name ?? "", jurisdiction.country_iso ?? ""));
|
|
1720
1778
|
}
|
|
1721
1779
|
}
|
|
1722
1780
|
if (jurisdiction.status === "restricted" && jurisdiction.allow_registration === false) {
|
|
1723
|
-
if (!blockReasons.
|
|
1724
|
-
blockReasons.push(
|
|
1781
|
+
if (!blockReasons.some((r) => r.code === "JURISDICTION_RESTRICTED")) {
|
|
1782
|
+
blockReasons.push(sdkReasons.jurisdictionRestricted());
|
|
1725
1783
|
}
|
|
1726
1784
|
}
|
|
1727
1785
|
}
|
|
@@ -1729,25 +1787,30 @@ var ComplianceClient = class {
|
|
|
1729
1787
|
blockReasons.push(...geoVerification.geofence_evaluation.reasons ?? []);
|
|
1730
1788
|
}
|
|
1731
1789
|
if (geoVerification.risk_level === "critical") {
|
|
1732
|
-
if (!blockReasons.
|
|
1733
|
-
blockReasons.push(
|
|
1790
|
+
if (!blockReasons.some((r) => r.code === "RISK_CRITICAL_LEVEL")) {
|
|
1791
|
+
blockReasons.push(sdkReasons.riskCriticalLevel());
|
|
1734
1792
|
}
|
|
1735
1793
|
}
|
|
1736
1794
|
if (cipherTextResult) {
|
|
1737
1795
|
if (!cipherTextResult.valid) {
|
|
1738
|
-
blockReasons.push(
|
|
1796
|
+
blockReasons.push(sdkReasons.ciphertextInvalid());
|
|
1739
1797
|
}
|
|
1740
1798
|
if (cipherTextResult.risk?.is_blocked) {
|
|
1741
1799
|
blockReasons.push(...cipherTextResult.risk.block_reasons || []);
|
|
1742
1800
|
}
|
|
1743
1801
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
1744
|
-
blockReasons.push(
|
|
1802
|
+
blockReasons.push(sdkReasons.gpsIPMismatch());
|
|
1745
1803
|
}
|
|
1746
1804
|
}
|
|
1747
1805
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
1748
|
-
blockReasons.push(
|
|
1806
|
+
blockReasons.push(sdkReasons.gpsRequired());
|
|
1749
1807
|
}
|
|
1750
|
-
|
|
1808
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1809
|
+
return blockReasons.filter((r) => {
|
|
1810
|
+
if (seen.has(r.code)) return false;
|
|
1811
|
+
seen.add(r.code);
|
|
1812
|
+
return true;
|
|
1813
|
+
});
|
|
1751
1814
|
}
|
|
1752
1815
|
/**
|
|
1753
1816
|
* Validate registration request has all required fields
|
|
@@ -2131,7 +2194,7 @@ var ComplianceClient = class {
|
|
|
2131
2194
|
const blockReasons = [...geoVerification.risk_reasons ?? []];
|
|
2132
2195
|
if (cipherTextResult) {
|
|
2133
2196
|
if (!cipherTextResult.valid) {
|
|
2134
|
-
blockReasons.push(
|
|
2197
|
+
blockReasons.push(sdkReasons.ciphertextInvalid());
|
|
2135
2198
|
}
|
|
2136
2199
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2137
2200
|
blockReasons.push(...cipherTextResult.risk.block_reasons || []);
|
|
@@ -2139,12 +2202,18 @@ var ComplianceClient = class {
|
|
|
2139
2202
|
}
|
|
2140
2203
|
const gpsBlocked = geoVerification.gps_required && !cipherTextResult;
|
|
2141
2204
|
if (gpsBlocked) {
|
|
2142
|
-
blockReasons.push(
|
|
2205
|
+
blockReasons.push(sdkReasons.gpsRequired());
|
|
2143
2206
|
}
|
|
2207
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2208
|
+
const dedupedBlockReasons = blockReasons.filter((r) => {
|
|
2209
|
+
if (seen.has(r.code)) return false;
|
|
2210
|
+
seen.add(r.code);
|
|
2211
|
+
return true;
|
|
2212
|
+
});
|
|
2144
2213
|
return {
|
|
2145
2214
|
allowed: geoVerification.is_compliant && !geoVerification.is_blocked && !cipherTextBlocked && !gpsBlocked,
|
|
2146
2215
|
geolocation: geoVerification,
|
|
2147
|
-
blockReasons:
|
|
2216
|
+
blockReasons: dedupedBlockReasons,
|
|
2148
2217
|
processingTime: Date.now() - startTime,
|
|
2149
2218
|
cipherTextValidation: cipherTextResult
|
|
2150
2219
|
};
|
|
@@ -2247,31 +2316,31 @@ var ComplianceClient = class {
|
|
|
2247
2316
|
const normalizedAmount = this.normalizeToUSD(amount, currency);
|
|
2248
2317
|
if (normalizedAmount > 1e4) {
|
|
2249
2318
|
riskScore += 30;
|
|
2250
|
-
factors.push(
|
|
2319
|
+
factors.push(sdkReasons.transactionHighAmount(normalizedAmount, currency, 1e4));
|
|
2251
2320
|
} else if (normalizedAmount > 5e3) {
|
|
2252
2321
|
riskScore += 15;
|
|
2253
|
-
factors.push(
|
|
2322
|
+
factors.push(sdkReasons.transactionElevatedAmount(normalizedAmount, currency, 5e3));
|
|
2254
2323
|
}
|
|
2255
2324
|
riskScore += geoVerification.risk_score * 0.4;
|
|
2256
2325
|
if (geoVerification.risk_level === "high" || geoVerification.risk_level === "critical") {
|
|
2257
|
-
factors.push(
|
|
2326
|
+
factors.push(sdkReasons.riskHighLocation());
|
|
2258
2327
|
}
|
|
2259
2328
|
riskScore += profile.risk_score * 0.3;
|
|
2260
2329
|
if (profile.risk_category === "high" || profile.risk_category === "critical") {
|
|
2261
|
-
factors.push(
|
|
2330
|
+
factors.push(sdkReasons.riskHighCustomer());
|
|
2262
2331
|
}
|
|
2263
2332
|
if (geoVerification.location.is_vpn || geoVerification.location.is_proxy) {
|
|
2264
2333
|
riskScore += 20;
|
|
2265
|
-
factors.push(
|
|
2334
|
+
factors.push(sdkReasons.anonymizationDetected());
|
|
2266
2335
|
}
|
|
2267
2336
|
if (profile.customer_status === "suspended") {
|
|
2268
2337
|
riskScore += 50;
|
|
2269
|
-
factors.push(
|
|
2338
|
+
factors.push(sdkReasons.accountSuspended());
|
|
2270
2339
|
}
|
|
2271
2340
|
if (cipherTextResult) {
|
|
2272
2341
|
if (cipherTextResult.risk?.location_mismatch) {
|
|
2273
2342
|
riskScore += 20;
|
|
2274
|
-
factors.push(
|
|
2343
|
+
factors.push(sdkReasons.gpsIPMismatch());
|
|
2275
2344
|
}
|
|
2276
2345
|
if (cipherTextResult.risk?.score) {
|
|
2277
2346
|
riskScore += cipherTextResult.risk.score * 0.2;
|
|
@@ -2309,48 +2378,58 @@ var ComplianceClient = class {
|
|
|
2309
2378
|
}
|
|
2310
2379
|
if (profile) {
|
|
2311
2380
|
if (profile.customer_status === "suspended") {
|
|
2312
|
-
reasons.push(
|
|
2381
|
+
reasons.push(sdkReasons.accountSuspended());
|
|
2313
2382
|
}
|
|
2314
2383
|
if (profile.has_sanctions) {
|
|
2315
|
-
reasons.push(
|
|
2384
|
+
reasons.push(sdkReasons.sanctionsMatch());
|
|
2316
2385
|
}
|
|
2317
2386
|
}
|
|
2318
2387
|
if (cipherTextResult) {
|
|
2319
2388
|
if (!cipherTextResult.valid) {
|
|
2320
|
-
reasons.push(
|
|
2389
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2321
2390
|
}
|
|
2322
2391
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2323
2392
|
reasons.push(...cipherTextResult.risk.block_reasons || []);
|
|
2324
2393
|
}
|
|
2325
2394
|
}
|
|
2326
2395
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2327
|
-
reasons.push(
|
|
2396
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2328
2397
|
}
|
|
2329
|
-
|
|
2398
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2399
|
+
return reasons.filter((r) => {
|
|
2400
|
+
if (seen.has(r.code)) return false;
|
|
2401
|
+
seen.add(r.code);
|
|
2402
|
+
return true;
|
|
2403
|
+
});
|
|
2330
2404
|
}
|
|
2331
2405
|
getTransactionBlockReasons(geoVerification, transactionRisk, jurisdictionAllowed, cipherTextResult) {
|
|
2332
2406
|
const reasons = [];
|
|
2333
2407
|
if (!geoVerification.is_compliant) {
|
|
2334
|
-
reasons.push(
|
|
2408
|
+
reasons.push(sdkReasons.jurisdictionNonCompliant());
|
|
2335
2409
|
}
|
|
2336
2410
|
if (!jurisdictionAllowed) {
|
|
2337
|
-
reasons.push(
|
|
2411
|
+
reasons.push(sdkReasons.transactionJurisdictionLimit());
|
|
2338
2412
|
}
|
|
2339
2413
|
if (!transactionRisk.allowed) {
|
|
2340
2414
|
reasons.push(...transactionRisk.factors);
|
|
2341
2415
|
}
|
|
2342
2416
|
if (cipherTextResult) {
|
|
2343
2417
|
if (!cipherTextResult.valid) {
|
|
2344
|
-
reasons.push(
|
|
2418
|
+
reasons.push(sdkReasons.ciphertextInvalid());
|
|
2345
2419
|
}
|
|
2346
2420
|
if (cipherTextResult.risk?.is_blocked) {
|
|
2347
2421
|
reasons.push(...cipherTextResult.risk.block_reasons || []);
|
|
2348
2422
|
}
|
|
2349
2423
|
}
|
|
2350
2424
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
2351
|
-
reasons.push(
|
|
2425
|
+
reasons.push(sdkReasons.gpsRequired());
|
|
2352
2426
|
}
|
|
2353
|
-
|
|
2427
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2428
|
+
return reasons.filter((r) => {
|
|
2429
|
+
if (seen.has(r.code)) return false;
|
|
2430
|
+
seen.add(r.code);
|
|
2431
|
+
return true;
|
|
2432
|
+
});
|
|
2354
2433
|
}
|
|
2355
2434
|
// ============================================================================
|
|
2356
2435
|
// Location Request Methods
|
|
@@ -2628,23 +2707,19 @@ var KycClient = class extends BaseClient {
|
|
|
2628
2707
|
*
|
|
2629
2708
|
* Generates a link that the user can visit to submit their KYC documents.
|
|
2630
2709
|
*
|
|
2631
|
-
* @param request - Request containing the user ID, redirect URL, callback URL
|
|
2632
|
-
* @returns Response containing
|
|
2710
|
+
* @param request - Request containing the user ID, optional redirect URL, and optional callback URL (receives POST requests)
|
|
2711
|
+
* @returns Response containing the redirect link and KYC ID
|
|
2633
2712
|
*
|
|
2634
2713
|
* @example
|
|
2635
2714
|
* ```typescript
|
|
2636
2715
|
* const result = await client.requestKycSubmitLink({
|
|
2637
2716
|
* user_id: "user_123",
|
|
2638
|
-
* redirect_url: "https://merchant.com/kyc-complete",
|
|
2639
|
-
* callback_url: "https://merchant.com/api/kyc-webhook"
|
|
2640
|
-
* trigger_event: "onboarding"
|
|
2717
|
+
* redirect_url: "https://merchant.com/kyc-complete", // optional
|
|
2718
|
+
* callback_url: "https://merchant.com/api/kyc-webhook" // optional - receives POST requests on status change
|
|
2641
2719
|
* });
|
|
2642
2720
|
*
|
|
2643
|
-
*
|
|
2644
|
-
*
|
|
2645
|
-
* } else if (result.can_skip) {
|
|
2646
|
-
* console.log("KYC not required, user can proceed");
|
|
2647
|
-
* }
|
|
2721
|
+
* console.log(`Redirect user to: ${result.link}`);
|
|
2722
|
+
* console.log(`KYC ID: ${result.kyc_id}`);
|
|
2648
2723
|
* ```
|
|
2649
2724
|
*/
|
|
2650
2725
|
async requestKycSubmitLink(request) {
|
|
@@ -3047,8 +3122,9 @@ var KycClient = class extends BaseClient {
|
|
|
3047
3122
|
*/
|
|
3048
3123
|
async riskProfileRequest(path, options = {}) {
|
|
3049
3124
|
if (!this.riskProfileBaseURL) {
|
|
3050
|
-
throw new
|
|
3051
|
-
"Risk Profile Service URL not configured. Please provide riskProfileBaseURL in KycClientConfig."
|
|
3125
|
+
throw new ValidationError(
|
|
3126
|
+
"Risk Profile Service URL not configured. Please provide riskProfileBaseURL in KycClientConfig.",
|
|
3127
|
+
["riskProfileBaseURL"]
|
|
3052
3128
|
);
|
|
3053
3129
|
}
|
|
3054
3130
|
return this.request(path, {
|
|
@@ -3160,6 +3236,19 @@ var KycClient = class extends BaseClient {
|
|
|
3160
3236
|
// ============================================================================
|
|
3161
3237
|
};
|
|
3162
3238
|
|
|
3239
|
+
// src/kyc/types.ts
|
|
3240
|
+
var KYC_DECLINED_DESCRIPTIONS = {
|
|
3241
|
+
KYC_DOCUMENT_EXPIRED: "The submitted document has expired",
|
|
3242
|
+
KYC_DOCUMENT_INVALID: "The submitted document could not be verified",
|
|
3243
|
+
KYC_FACE_MISMATCH: "Face verification did not match the identity document",
|
|
3244
|
+
KYC_AGE_REQUIREMENT: "Age requirement not met",
|
|
3245
|
+
KYC_DUPLICATE_IDENTITY: "This identity has already been verified under another account",
|
|
3246
|
+
KYC_ADDRESS_MISMATCH: "Address verification failed",
|
|
3247
|
+
KYC_NAME_MISMATCH: "Name on document does not match the registered name",
|
|
3248
|
+
KYC_PROVIDER_REJECTED: "Identity verification was rejected by the verification provider",
|
|
3249
|
+
KYC_DECLINED: "Identity verification was declined"
|
|
3250
|
+
};
|
|
3251
|
+
|
|
3163
3252
|
// src/tax/client.ts
|
|
3164
3253
|
var TaxClient = class extends BaseClient {
|
|
3165
3254
|
constructor(config) {
|
|
@@ -3226,41 +3315,6 @@ var TaxClient = class extends BaseClient {
|
|
|
3226
3315
|
{ ...requestOptions, responseType: "arraybuffer" }
|
|
3227
3316
|
);
|
|
3228
3317
|
}
|
|
3229
|
-
async runReminders() {
|
|
3230
|
-
return this.request("/api/v1/tax/reminders/run", {
|
|
3231
|
-
method: "POST"
|
|
3232
|
-
});
|
|
3233
|
-
}
|
|
3234
|
-
/**
|
|
3235
|
-
* Update only the reminder configuration (frequency + max count) without
|
|
3236
|
-
* touching any other tax rule settings. Fetches current rules first and
|
|
3237
|
-
* merges the reminder fields before sending the update.
|
|
3238
|
-
*
|
|
3239
|
-
* Requires the caller to be authenticated as a Tenant Super Admin.
|
|
3240
|
-
* Throws VesantError with status 403 if the role requirement is not met.
|
|
3241
|
-
*/
|
|
3242
|
-
async updateReminderConfig(input) {
|
|
3243
|
-
const current = await this.getTaxRules();
|
|
3244
|
-
return this.updateTaxRules({
|
|
3245
|
-
trigger_account_creation: current.trigger_account_creation,
|
|
3246
|
-
trigger_first_withdrawal: current.trigger_first_withdrawal,
|
|
3247
|
-
trigger_threshold: current.trigger_threshold,
|
|
3248
|
-
trigger_manual: current.trigger_manual,
|
|
3249
|
-
trigger_tin_invalid: current.trigger_tin_invalid,
|
|
3250
|
-
trigger_w8ben_expiry: current.trigger_w8ben_expiry,
|
|
3251
|
-
trigger_tin_expired: current.trigger_tin_expired,
|
|
3252
|
-
us_withholding_enabled: current.us_withholding_enabled,
|
|
3253
|
-
non_us_withholding_enabled: current.non_us_withholding_enabled,
|
|
3254
|
-
transaction_action: current.transaction_action,
|
|
3255
|
-
w8ben_expiry_warning_days: current.w8ben_expiry_warning_days,
|
|
3256
|
-
tin_verification_due_date: current.tin_verification_due_date,
|
|
3257
|
-
email_sending_method: current.email_sending_method,
|
|
3258
|
-
display_tin_links_on_platform: current.display_tin_links_on_platform,
|
|
3259
|
-
tax_treaty_support: current.tax_treaty_support,
|
|
3260
|
-
reminder_frequency_days: input.reminder_frequency_days,
|
|
3261
|
-
reminder_max_count: input.reminder_max_count
|
|
3262
|
-
});
|
|
3263
|
-
}
|
|
3264
3318
|
// ==========================================================================
|
|
3265
3319
|
// P2 — Tenant Tax Rules
|
|
3266
3320
|
// ==========================================================================
|
|
@@ -3304,136 +3358,15 @@ var TaxClient = class extends BaseClient {
|
|
|
3304
3358
|
}
|
|
3305
3359
|
};
|
|
3306
3360
|
|
|
3307
|
-
// src/transaction/client.ts
|
|
3308
|
-
var TransactionClient = class extends BaseClient {
|
|
3309
|
-
constructor(config) {
|
|
3310
|
-
super(config);
|
|
3311
|
-
}
|
|
3312
|
-
// ==========================================================================
|
|
3313
|
-
// Transaction creation
|
|
3314
|
-
// ==========================================================================
|
|
3315
|
-
/**
|
|
3316
|
-
* Submit a new transaction record to the monitoring service.
|
|
3317
|
-
*
|
|
3318
|
-
* The gateway endpoint is `POST /api/v1/tm/transactions` and returns 201
|
|
3319
|
-
* with no body on success. The SDK method resolves to `void` to reflect
|
|
3320
|
-
* that behaviour.
|
|
3321
|
-
*
|
|
3322
|
-
* @param request - Data required to create the transaction
|
|
3323
|
-
* @param requestOptions - Optional request options (e.g. AbortSignal)
|
|
3324
|
-
*
|
|
3325
|
-
* @example
|
|
3326
|
-
* ```ts
|
|
3327
|
-
* const client = new TransactionClient({
|
|
3328
|
-
* baseURL: process.env.API_GATEWAY_URL!,
|
|
3329
|
-
* tenantId: 'tenant-123',
|
|
3330
|
-
* apiKey: 'pk_test_...',
|
|
3331
|
-
* });
|
|
3332
|
-
*
|
|
3333
|
-
* await client.createTransaction({
|
|
3334
|
-
* tx_id: 'tx-1',
|
|
3335
|
-
* reference: 'ref-1',
|
|
3336
|
-
* tenant_id: 'tenant-123',
|
|
3337
|
-
* customer_id: 'cust-1',
|
|
3338
|
-
* transaction_type: 'deposit',
|
|
3339
|
-
* transaction_mode: 'ach',
|
|
3340
|
-
* amount: '100.00',
|
|
3341
|
-
* currency: 'USD',
|
|
3342
|
-
* status: 'pending',
|
|
3343
|
-
* source_account: 'acct-1',
|
|
3344
|
-
* destination_account: 'acct-2',
|
|
3345
|
-
* country: 'US',
|
|
3346
|
-
* ip_address: '10.0.0.1',
|
|
3347
|
-
* metadata: {},
|
|
3348
|
-
* benificiary_comment: '',
|
|
3349
|
-
* transaction_date: new Date().toISOString(),
|
|
3350
|
-
* });
|
|
3351
|
-
* ```
|
|
3352
|
-
*/
|
|
3353
|
-
async createTransaction(request, requestOptions) {
|
|
3354
|
-
const data = await this.requestWithRetry("/api/v1/tm/transactions", {
|
|
3355
|
-
method: "POST",
|
|
3356
|
-
body: JSON.stringify(request)
|
|
3357
|
-
}, void 0, void 0, requestOptions);
|
|
3358
|
-
return data;
|
|
3359
|
-
}
|
|
3360
|
-
/**
|
|
3361
|
-
*
|
|
3362
|
-
* @param transactionId tx_id of the transaction
|
|
3363
|
-
* @param requestOptions optional request options (e.g. AbortSignal)
|
|
3364
|
-
*/
|
|
3365
|
-
async getTransaction(transactionId, requestOptions) {
|
|
3366
|
-
await this.requestWithRetry(`/api/v1/tm/transactions/status/${transactionId}`, {
|
|
3367
|
-
method: "GET"
|
|
3368
|
-
}, void 0, void 0, requestOptions);
|
|
3369
|
-
}
|
|
3370
|
-
};
|
|
3371
|
-
|
|
3372
|
-
// src/fraud/client.ts
|
|
3373
|
-
var FraudClient = class extends BaseClient {
|
|
3374
|
-
/**
|
|
3375
|
-
* Submit a single fraud event for scoring.
|
|
3376
|
-
*/
|
|
3377
|
-
async scoreEvent(request, requestOptions) {
|
|
3378
|
-
this.validateScoreRequest(request);
|
|
3379
|
-
return this.requestWithRetry(
|
|
3380
|
-
"/api/v1/fraud/score",
|
|
3381
|
-
{
|
|
3382
|
-
method: "POST",
|
|
3383
|
-
body: JSON.stringify(request)
|
|
3384
|
-
},
|
|
3385
|
-
void 0,
|
|
3386
|
-
void 0,
|
|
3387
|
-
requestOptions
|
|
3388
|
-
);
|
|
3389
|
-
}
|
|
3390
|
-
/**
|
|
3391
|
-
* Submit multiple fraud events for scoring.
|
|
3392
|
-
*/
|
|
3393
|
-
async scoreEventsBulk(requests, requestOptions) {
|
|
3394
|
-
if (!Array.isArray(requests) || requests.length === 0) {
|
|
3395
|
-
throw new ValidationError(
|
|
3396
|
-
"Invalid bulk score request: at least one score request is required",
|
|
3397
|
-
["requests"]
|
|
3398
|
-
);
|
|
3399
|
-
}
|
|
3400
|
-
requests.forEach((request, index) => this.validateScoreRequest(request, index));
|
|
3401
|
-
return this.requestWithRetry(
|
|
3402
|
-
"/api/v1/fraud/score/bulk",
|
|
3403
|
-
{
|
|
3404
|
-
method: "POST",
|
|
3405
|
-
body: JSON.stringify(requests)
|
|
3406
|
-
},
|
|
3407
|
-
void 0,
|
|
3408
|
-
void 0,
|
|
3409
|
-
requestOptions
|
|
3410
|
-
);
|
|
3411
|
-
}
|
|
3412
|
-
validateScoreRequest(request, index) {
|
|
3413
|
-
const errors = [];
|
|
3414
|
-
const prefix = index === void 0 ? "" : `requests[${index}].`;
|
|
3415
|
-
if (!request.customer_id?.trim()) {
|
|
3416
|
-
errors.push(`${prefix}customer_id is required`);
|
|
3417
|
-
}
|
|
3418
|
-
if (!request.sift_user_id?.trim()) {
|
|
3419
|
-
errors.push(`${prefix}sift_user_id is required`);
|
|
3420
|
-
}
|
|
3421
|
-
if (!request.event_type?.trim()) {
|
|
3422
|
-
errors.push(`${prefix}event_type is required`);
|
|
3423
|
-
}
|
|
3424
|
-
if (errors.length > 0) {
|
|
3425
|
-
throw new ValidationError(`Invalid fraud score request: ${errors.join(", ")}`, errors);
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
};
|
|
3429
|
-
|
|
3430
3361
|
// src/webhooks/handler.ts
|
|
3431
3362
|
var WebhookHandler = class {
|
|
3432
3363
|
constructor(config) {
|
|
3433
3364
|
this.handlers = /* @__PURE__ */ new Map();
|
|
3434
3365
|
this.anyHandlers = [];
|
|
3366
|
+
this.seenEventIds = /* @__PURE__ */ new Map();
|
|
3435
3367
|
this.secret = config.secret;
|
|
3436
3368
|
this.tolerance = config.tolerance ?? 3e5;
|
|
3369
|
+
this.replayProtection = config.replayProtection ?? true;
|
|
3437
3370
|
}
|
|
3438
3371
|
/**
|
|
3439
3372
|
* Register a handler for a specific event type.
|
|
@@ -3457,7 +3390,7 @@ var WebhookHandler = class {
|
|
|
3457
3390
|
async verifyAndParse(body, signature) {
|
|
3458
3391
|
const isValid = await verifyWebhookSignature(body, signature, this.secret);
|
|
3459
3392
|
if (!isValid) {
|
|
3460
|
-
throw new
|
|
3393
|
+
throw new ValidationError("Invalid webhook signature", ["signature"]);
|
|
3461
3394
|
}
|
|
3462
3395
|
return this.parseAndValidate(body);
|
|
3463
3396
|
}
|
|
@@ -3477,16 +3410,34 @@ var WebhookHandler = class {
|
|
|
3477
3410
|
parseAndValidate(body) {
|
|
3478
3411
|
const event = JSON.parse(body);
|
|
3479
3412
|
if (!event.type || !event.id || !event.timestamp) {
|
|
3480
|
-
throw new
|
|
3413
|
+
throw new ValidationError("Invalid webhook event: missing required fields (type, id, timestamp)", ["type", "id", "timestamp"]);
|
|
3481
3414
|
}
|
|
3482
3415
|
if (this.tolerance > 0) {
|
|
3483
3416
|
const eventTime = new Date(event.timestamp).getTime();
|
|
3484
3417
|
const now = Date.now();
|
|
3485
3418
|
if (Math.abs(now - eventTime) > this.tolerance) {
|
|
3486
|
-
throw new
|
|
3487
|
-
`Webhook event timestamp is outside tolerance window (${this.tolerance}ms)
|
|
3419
|
+
throw new ValidationError(
|
|
3420
|
+
`Webhook event timestamp is outside tolerance window (${this.tolerance}ms)`,
|
|
3421
|
+
["timestamp"]
|
|
3422
|
+
);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
if (this.replayProtection) {
|
|
3426
|
+
if (this.seenEventIds.has(event.id)) {
|
|
3427
|
+
throw new ValidationError(
|
|
3428
|
+
`Duplicate webhook event: ${event.id} has already been processed`,
|
|
3429
|
+
["id"]
|
|
3488
3430
|
);
|
|
3489
3431
|
}
|
|
3432
|
+
const now = Date.now();
|
|
3433
|
+
this.seenEventIds.set(event.id, now);
|
|
3434
|
+
if (this.seenEventIds.size > 1e3) {
|
|
3435
|
+
for (const [id, seenAt] of this.seenEventIds) {
|
|
3436
|
+
if (now - seenAt > this.tolerance) {
|
|
3437
|
+
this.seenEventIds.delete(id);
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3490
3441
|
}
|
|
3491
3442
|
return event;
|
|
3492
3443
|
}
|
|
@@ -3519,6 +3470,8 @@ function createWebhookMiddleware(options) {
|
|
|
3519
3470
|
res.status(401).json({ error: message });
|
|
3520
3471
|
} else if (message.includes("tolerance") || message.includes("timestamp")) {
|
|
3521
3472
|
res.status(400).json({ error: message });
|
|
3473
|
+
} else if (message.includes("Duplicate")) {
|
|
3474
|
+
res.status(409).json({ error: message });
|
|
3522
3475
|
} else if (next) {
|
|
3523
3476
|
next(error);
|
|
3524
3477
|
} else {
|
|
@@ -3547,7 +3500,7 @@ function createNextWebhookHandler(options) {
|
|
|
3547
3500
|
});
|
|
3548
3501
|
} catch (error) {
|
|
3549
3502
|
const message = error instanceof Error ? error.message : "Webhook processing failed";
|
|
3550
|
-
const status = message.includes("signature") ? 401 : message.includes("tolerance") || message.includes("timestamp") ? 400 : 500;
|
|
3503
|
+
const status = message.includes("signature") ? 401 : message.includes("tolerance") || message.includes("timestamp") ? 400 : message.includes("Duplicate") ? 409 : 500;
|
|
3551
3504
|
return new Response(JSON.stringify({ error: message }), {
|
|
3552
3505
|
status,
|
|
3553
3506
|
headers: { "Content-Type": "application/json" }
|
|
@@ -3579,8 +3532,8 @@ exports.ComplianceBlockedError = ComplianceBlockedError;
|
|
|
3579
3532
|
exports.ComplianceClient = ComplianceClient;
|
|
3580
3533
|
exports.ComplianceError = ComplianceError;
|
|
3581
3534
|
exports.DEFAULT_CURRENCY_RATES = DEFAULT_CURRENCY_RATES;
|
|
3582
|
-
exports.FraudClient = FraudClient;
|
|
3583
3535
|
exports.GeolocationClient = GeolocationClient;
|
|
3536
|
+
exports.KYC_DECLINED_DESCRIPTIONS = KYC_DECLINED_DESCRIPTIONS;
|
|
3584
3537
|
exports.KycClient = KycClient;
|
|
3585
3538
|
exports.NetworkError = NetworkError;
|
|
3586
3539
|
exports.RateLimitError = RateLimitError;
|
|
@@ -3590,7 +3543,6 @@ exports.SDK_VERSION = SDK_VERSION;
|
|
|
3590
3543
|
exports.ServiceUnavailableError = ServiceUnavailableError;
|
|
3591
3544
|
exports.TaxClient = TaxClient;
|
|
3592
3545
|
exports.TimeoutError = TimeoutError;
|
|
3593
|
-
exports.TransactionClient = TransactionClient;
|
|
3594
3546
|
exports.ValidationError = ValidationError;
|
|
3595
3547
|
exports.VesantError = VesantError;
|
|
3596
3548
|
exports.WebhookHandler = WebhookHandler;
|
|
@@ -3601,6 +3553,7 @@ exports.decodeCipherText = decodeCipherText;
|
|
|
3601
3553
|
exports.generateCipherText = generateCipherText;
|
|
3602
3554
|
exports.isCipherTextExpired = isCipherTextExpired;
|
|
3603
3555
|
exports.noopLogger = noopLogger;
|
|
3556
|
+
exports.sdkReasons = sdkReasons;
|
|
3604
3557
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
3605
3558
|
//# sourceMappingURL=index.js.map
|
|
3606
3559
|
//# sourceMappingURL=index.js.map
|