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.
Files changed (86) hide show
  1. package/README.md +14 -4
  2. package/dist/client-BJ87_Vv5.d.ts +430 -0
  3. package/dist/{client-ePzhQKp9.d.mts → client-BolQlL5e.d.mts} +1 -1
  4. package/dist/{client-ePzhQKp9.d.ts → client-BolQlL5e.d.ts} +1 -1
  5. package/dist/{client-BlCxjbY2.d.mts → client-Bvp-f05-.d.ts} +7 -7
  6. package/dist/{client-C_A7QLcB.d.ts → client-CIEa7xYG.d.mts} +7 -7
  7. package/dist/client-IAOGCBfm.d.mts +430 -0
  8. package/dist/compliance/index.d.mts +25 -429
  9. package/dist/compliance/index.d.ts +25 -429
  10. package/dist/compliance/index.js +137 -58
  11. package/dist/compliance/index.js.map +1 -1
  12. package/dist/compliance/index.mjs +137 -59
  13. package/dist/compliance/index.mjs.map +1 -1
  14. package/dist/decisions/index.d.mts +2 -2
  15. package/dist/decisions/index.d.ts +2 -2
  16. package/dist/decisions/index.js +1 -1
  17. package/dist/decisions/index.js.map +1 -1
  18. package/dist/decisions/index.mjs +1 -1
  19. package/dist/decisions/index.mjs.map +1 -1
  20. package/dist/geolocation/index.d.mts +4 -4
  21. package/dist/geolocation/index.d.ts +4 -4
  22. package/dist/geolocation/index.js +6 -24
  23. package/dist/geolocation/index.js.map +1 -1
  24. package/dist/geolocation/index.mjs +6 -24
  25. package/dist/geolocation/index.mjs.map +1 -1
  26. package/dist/index.d.mts +14 -71
  27. package/dist/index.d.ts +14 -71
  28. package/dist/index.js +190 -237
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +189 -236
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/kyc/core.d.mts +4 -4
  33. package/dist/kyc/core.d.ts +4 -4
  34. package/dist/kyc/core.js +24 -13
  35. package/dist/kyc/core.js.map +1 -1
  36. package/dist/kyc/core.mjs +24 -14
  37. package/dist/kyc/core.mjs.map +1 -1
  38. package/dist/kyc/index.d.mts +20 -27
  39. package/dist/kyc/index.d.ts +20 -27
  40. package/dist/kyc/index.js +24 -13
  41. package/dist/kyc/index.js.map +1 -1
  42. package/dist/kyc/index.mjs +24 -14
  43. package/dist/kyc/index.mjs.map +1 -1
  44. package/dist/react.d.mts +6 -6
  45. package/dist/react.d.ts +6 -6
  46. package/dist/react.js +18 -5
  47. package/dist/react.js.map +1 -1
  48. package/dist/react.mjs +18 -5
  49. package/dist/react.mjs.map +1 -1
  50. package/dist/risk-profile/index.d.mts +4 -4
  51. package/dist/risk-profile/index.d.ts +4 -4
  52. package/dist/risk-profile/index.js +1 -1
  53. package/dist/risk-profile/index.js.map +1 -1
  54. package/dist/risk-profile/index.mjs +1 -1
  55. package/dist/risk-profile/index.mjs.map +1 -1
  56. package/dist/scores/index.d.mts +2 -2
  57. package/dist/scores/index.d.ts +2 -2
  58. package/dist/scores/index.js +1 -1
  59. package/dist/scores/index.js.map +1 -1
  60. package/dist/scores/index.mjs +1 -1
  61. package/dist/scores/index.mjs.map +1 -1
  62. package/dist/tax/index.d.mts +6 -41
  63. package/dist/tax/index.d.ts +6 -41
  64. package/dist/tax/index.js +1 -36
  65. package/dist/tax/index.js.map +1 -1
  66. package/dist/tax/index.mjs +1 -36
  67. package/dist/tax/index.mjs.map +1 -1
  68. package/dist/{types-X5Md_dD_.d.ts → types-2utj53GK.d.ts} +2 -2
  69. package/dist/{types-1RzYeSal.d.mts → types-C4Zx0d_u.d.mts} +2 -2
  70. package/dist/{types-B4Ezqo7V.d.mts → types-QUCWam16.d.mts} +7 -1
  71. package/dist/{types-B4Ezqo7V.d.ts → types-QUCWam16.d.ts} +7 -1
  72. package/dist/webhooks/index.d.mts +181 -2
  73. package/dist/webhooks/index.d.ts +181 -2
  74. package/dist/webhooks/index.js +49 -7
  75. package/dist/webhooks/index.js.map +1 -1
  76. package/dist/webhooks/index.mjs +49 -7
  77. package/dist/webhooks/index.mjs.map +1 -1
  78. package/package.json +16 -13
  79. package/dist/fraud/index.d.mts +0 -80
  80. package/dist/fraud/index.d.ts +0 -80
  81. package/dist/fraud/index.js +0 -606
  82. package/dist/fraud/index.js.map +0 -1
  83. package/dist/fraud/index.mjs +0 -604
  84. package/dist/fraud/index.mjs.map +0 -1
  85. package/dist/index-B04H4xfJ.d.mts +0 -320
  86. 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 = "1.6.6";
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 Error(
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 Error("No base64 encoding method available");
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 Error(
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(enrichedRequest)
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 Error ? error.code : void 0,
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.includes("non_compliant_jurisdiction")) {
1709
- blockReasons.push("non_compliant_jurisdiction");
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("registration_not_allowed_in_jurisdiction");
1773
+ blockReasons.push(sdkReasons.jurisdictionRegistrationDenied());
1716
1774
  }
1717
1775
  if (jurisdiction.status === "blocked" || jurisdiction.status === "sanctioned") {
1718
- if (!blockReasons.includes("blocked_jurisdiction")) {
1719
- blockReasons.push("blocked_jurisdiction");
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.includes("restricted_jurisdiction_no_registration")) {
1724
- blockReasons.push("restricted_jurisdiction_no_registration");
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.includes("critical_risk_level")) {
1733
- blockReasons.push("critical_risk_level");
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("ciphertext_validation_failed");
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("gps_ip_location_mismatch");
1802
+ blockReasons.push(sdkReasons.gpsIPMismatch());
1745
1803
  }
1746
1804
  }
1747
1805
  if (geoVerification.gps_required && !cipherTextResult) {
1748
- blockReasons.push("gps_verification_required");
1806
+ blockReasons.push(sdkReasons.gpsRequired());
1749
1807
  }
1750
- return [...new Set(blockReasons)];
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("ciphertext_validation_failed");
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("gps_verification_required");
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: [...new Set(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("high_transaction_amount");
2319
+ factors.push(sdkReasons.transactionHighAmount(normalizedAmount, currency, 1e4));
2251
2320
  } else if (normalizedAmount > 5e3) {
2252
2321
  riskScore += 15;
2253
- factors.push("elevated_transaction_amount");
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("high_risk_location");
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("high_risk_customer");
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("anonymization_detected");
2334
+ factors.push(sdkReasons.anonymizationDetected());
2266
2335
  }
2267
2336
  if (profile.customer_status === "suspended") {
2268
2337
  riskScore += 50;
2269
- factors.push("account_suspended");
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("gps_ip_location_mismatch");
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("account_suspended");
2381
+ reasons.push(sdkReasons.accountSuspended());
2313
2382
  }
2314
2383
  if (profile.has_sanctions) {
2315
- reasons.push("sanctions_match");
2384
+ reasons.push(sdkReasons.sanctionsMatch());
2316
2385
  }
2317
2386
  }
2318
2387
  if (cipherTextResult) {
2319
2388
  if (!cipherTextResult.valid) {
2320
- reasons.push("ciphertext_validation_failed");
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("gps_verification_required");
2396
+ reasons.push(sdkReasons.gpsRequired());
2328
2397
  }
2329
- return [...new Set(reasons)];
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("non_compliant_jurisdiction");
2408
+ reasons.push(sdkReasons.jurisdictionNonCompliant());
2335
2409
  }
2336
2410
  if (!jurisdictionAllowed) {
2337
- reasons.push("exceeds_jurisdiction_limit");
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("ciphertext_validation_failed");
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("gps_verification_required");
2425
+ reasons.push(sdkReasons.gpsRequired());
2352
2426
  }
2353
- return [...new Set(reasons)];
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, and trigger event
2632
- * @returns Response containing kyc_required, can_skip, and optionally the redirect link and KYC ID
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
- * if (result.kyc_required && result.link) {
2644
- * console.log(`Redirect user to: ${result.link}`);
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 Error(
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 Error("Invalid webhook signature");
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 Error("Invalid webhook event: missing required fields (type, id, timestamp)");
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 Error(
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