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