@privateav/sdk 3.5.2 → 3.5.5

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 CHANGED
@@ -1,4 +1,4 @@
1
- # PrivateAV SDK v3.5.2
1
+ # PrivateAV SDK v3.5.5
2
2
 
3
3
  A lightweight SDK for integrating PrivateAV age verification into your website or application.
4
4
 
@@ -14,14 +14,22 @@ A lightweight SDK for integrating PrivateAV age verification into your website o
14
14
 
15
15
  ## Changelog
16
16
 
17
- ### 3.5.2
18
- - Fixed brand references in SDK documentation
17
+ ### 3.5.5
18
+ - Routes explicit staging sessions through the canonical VerityGuard-owned staging hosts for the selected SDK brand.
19
+ - Keeps production routing unchanged.
20
+
19
21
 
20
- ### 3.5.1
21
- - Fixed staging environment URLs to match actual infrastructure
22
+ ### 3.5.3
23
+ - Fixed environment routing so customer-owned hostnames always default to production infrastructure, even when the hostname contains `staging`/`stage`/`qa`/etc. Internal staging is selected only via an explicit `environment: 'staging'` or a first-party Verity staging host.
24
+ - No public SDK API changes.
25
+
26
+ ### 3.5.2
27
+ - PrivateAV version alignment release to match PrivateAV at `3.5.2`
28
+ - No SDK API changes from `3.5.0`
22
29
 
23
30
  ### 3.5.0
24
- - Bumped SDK version
31
+ - Added `language` option for UI localization (`en`, `de`, `es`, `fr`, `pt`, `it`)
32
+ - Per-verification `language` override via `verify({ language: 'es' })`
25
33
 
26
34
  ### 3.4.11
27
35
  - Fixed CDN documentation URLs (files are at package root, not `/dist/`)
@@ -38,7 +46,7 @@ npm install @privateav/sdk
38
46
  Or load directly from jsDelivr CDN (no bundler required):
39
47
 
40
48
  ```html
41
- <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@latest/privateav.min.js"></script>
49
+ <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.5/sdk.min.js"></script>
42
50
  ```
43
51
 
44
52
  ## Quick Start
@@ -60,7 +68,7 @@ await sp.verify();
60
68
  ### With CDN (no bundler)
61
69
 
62
70
  ```html
63
- <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@latest/privateav.min.js"></script>
71
+ <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.5/sdk.min.js"></script>
64
72
  <script>
65
73
  const sp = new PrivateAV({
66
74
  apiKey: 'pk_...',
@@ -86,7 +94,7 @@ That's it! The SDK handles session creation automatically.
86
94
  | defaultVerificationMode | string | No | `'L1'` or `'L2'` |
87
95
  | onComplete | function | No | Callback for new-tab mode |
88
96
  | onCancel | function | No | Called when user closes popup (new-tab mode). Return `false` to suppress automatic `cancelUrl` redirect. |
89
- | language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`, `'pt'`). Can be overridden per verification. |
97
+ | language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`, `'pt'`, `'it'`). Can be overridden per verification. |
90
98
  | onError | function | No | Error handler |
91
99
 
92
100
  ## Verification Options
@@ -97,6 +105,7 @@ Override settings per-verification:
97
105
  await sp.verify({
98
106
  challengeAge: 30, // Override minimum age for this session
99
107
  verificationMode: 'L2', // Force ID verification for this session
108
+ faceMatchEnabled: false, // Disable selfie-vs-ID matching where required
100
109
  externalUserId: 'user-123', // Your user ID (returned in webhooks)
101
110
  skipIntro: true, // Skip intro screen
102
111
  autoReturn: true, // Auto-redirect after success
@@ -168,17 +177,20 @@ app.get('/age-verified', async (req, res) => {
168
177
 
169
178
  // Validate with your SECRET key (sk_...)
170
179
  const response = await fetch(
171
- `https://api.privateav.com/api/v1/sessions/${sessionId}`,
180
+ 'https://api.privateav.com/api/v1/sessions/validate',
172
181
  {
182
+ method: 'POST',
173
183
  headers: {
174
- 'Authorization': `Bearer ${process.env.PRIVATEAV_SECRET_KEY}`
175
- }
184
+ 'Content-Type': 'application/json',
185
+ 'X-API-Key': process.env.PRIVATEAV_SECRET_KEY
186
+ },
187
+ body: JSON.stringify({ sessionId })
176
188
  }
177
189
  );
178
190
 
179
191
  const session = await response.json();
180
192
 
181
- if (session.status === 'VERIFIED') {
193
+ if (session.verified && session.accessGranted) {
182
194
  // Grant access
183
195
  req.session.ageVerified = true;
184
196
  res.redirect('/content');
@@ -214,7 +226,7 @@ For reliable verification tracking, configure webhooks in your dashboard:
214
226
  <html>
215
227
  <head>
216
228
  <title>Age Verification</title>
217
- <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@latest/privateav.min.js"></script>
229
+ <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.5/sdk.min.js"></script>
218
230
  </head>
219
231
  <body>
220
232
  <button id="verify-btn">Verify Your Age</button>
@@ -7,6 +7,6 @@ export type PrivateAVConfig = SDKConfig;
7
7
  export declare class PrivateAV extends VerificationSDK {
8
8
  constructor(config: PrivateAVConfig);
9
9
  }
10
- export declare const VERSION = "3.5.2";
10
+ export declare const VERSION = "3.5.5";
11
11
  export type { SDKConfig, VerificationOptions, VerificationResult, SessionValidationResponse, SessionCreationResponse, CreateSessionRequest, };
12
12
  export default PrivateAV;
@@ -78,10 +78,6 @@ export declare class VerificationSDK {
78
78
  * Set up automatic cleanup on page unload to prevent memory leaks
79
79
  */
80
80
  private setupAutoCleanup;
81
- /**
82
- * Auto-detect environment based on current URL
83
- */
84
- private detectEnvironment;
85
81
  /**
86
82
  * Get the current environment
87
83
  */
package/index.js CHANGED
@@ -30,8 +30,13 @@ var __objRest = (source, exclude) => {
30
30
  }
31
31
  return target;
32
32
  };
33
- var __esm = (fn, res) => function __init() {
34
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
33
+ var __esm = (fn, res, err) => function __init() {
34
+ if (err) throw err[0];
35
+ try {
36
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
37
+ } catch (e) {
38
+ throw err = [e], e;
39
+ }
35
40
  };
36
41
  var __export = (target, all) => {
37
42
  for (var name in all)
@@ -272,6 +277,7 @@ var init_crypto = __esm({
272
277
 
273
278
  // src-redirect/utils/validation.ts
274
279
  function validateConfig(config, context) {
280
+ var _a;
275
281
  if (!config.apiKey) {
276
282
  throw new Error("apiKey is required");
277
283
  }
@@ -298,7 +304,7 @@ function validateConfig(config, context) {
298
304
  `returnUrl exceeds maximum length of ${MAX_URL_LENGTH} characters`
299
305
  );
300
306
  }
301
- const environment = detectEnvironment();
307
+ const environment = (_a = context.environment) != null ? _a : "production";
302
308
  const returnUrlValidation = validateReturnUrl(config.returnUrl, environment, context.brandName);
303
309
  if (!returnUrlValidation.isValid) {
304
310
  throw new Error(
@@ -329,6 +335,9 @@ function validateConfig(config, context) {
329
335
  if (config.defaultVerificationMode && !["L1", "L2"].includes(config.defaultVerificationMode)) {
330
336
  throw new Error("defaultVerificationMode must be L1 or L2");
331
337
  }
338
+ if (config.faceMatchEnabled !== void 0 && typeof config.faceMatchEnabled !== "boolean") {
339
+ throw new Error("faceMatchEnabled must be a boolean");
340
+ }
332
341
  if (config.mode && !["redirect", "new-tab"].includes(config.mode)) {
333
342
  throw new Error("mode must be redirect or new-tab");
334
343
  }
@@ -336,16 +345,6 @@ function validateConfig(config, context) {
336
345
  throw new Error("newTabTarget must be popup or tab");
337
346
  }
338
347
  }
339
- function detectEnvironment() {
340
- if (typeof window === "undefined") {
341
- return "production";
342
- }
343
- const hostname = window.location.hostname;
344
- if (hostname.includes("staging") || hostname.includes("stage")) {
345
- return "staging";
346
- }
347
- return "production";
348
- }
349
348
  async function generateState(payload, environment, hmacSecret, _logLabel = "SDK") {
350
349
  const { createSignedState: createSignedState2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
351
350
  return createSignedState2(payload, hmacSecret);
@@ -426,6 +425,80 @@ function getApiUrl(environment, urls) {
426
425
  }
427
426
  return url;
428
427
  }
428
+ function hostnameFromUrl(rawUrl) {
429
+ if (!rawUrl) {
430
+ return null;
431
+ }
432
+ try {
433
+ return new URL(rawUrl).hostname.toLowerCase();
434
+ } catch (e) {
435
+ return null;
436
+ }
437
+ }
438
+ function parentZone(hostname) {
439
+ const labels = hostname.split(".");
440
+ if (labels.length <= 2) {
441
+ return hostname;
442
+ }
443
+ return labels.slice(1).join(".");
444
+ }
445
+ function collectZones(urls) {
446
+ const zones = /* @__PURE__ */ new Set();
447
+ for (const rawUrl of urls) {
448
+ const host = hostnameFromUrl(rawUrl);
449
+ if (host) {
450
+ zones.add(parentZone(host));
451
+ }
452
+ }
453
+ return zones;
454
+ }
455
+ function getFirstPartyStagingHosts(brandUrls) {
456
+ var _a, _b;
457
+ if (!brandUrls || !brandUrls.staging) {
458
+ return [];
459
+ }
460
+ const staging = brandUrls.staging;
461
+ const stagingZones = collectZones([
462
+ staging.apiUrl,
463
+ staging.verifyUiUrl,
464
+ staging.engineUrl,
465
+ staging.wsUrl,
466
+ ...(_a = staging.trustedOrigins) != null ? _a : []
467
+ ]);
468
+ const production = brandUrls.production;
469
+ const productionZones = production ? collectZones([
470
+ production.apiUrl,
471
+ production.verifyUiUrl,
472
+ production.engineUrl,
473
+ production.wsUrl,
474
+ ...(_b = production.trustedOrigins) != null ? _b : []
475
+ ]) : /* @__PURE__ */ new Set();
476
+ for (const zone of productionZones) {
477
+ stagingZones.delete(zone);
478
+ }
479
+ return Array.from(stagingZones);
480
+ }
481
+ function isFirstPartyStagingHost(hostname, brandUrls) {
482
+ const host = (hostname || "").toLowerCase();
483
+ if (!host) {
484
+ return false;
485
+ }
486
+ return getFirstPartyStagingHosts(brandUrls).some(
487
+ (zone) => host === zone || host.endsWith(`.${zone}`)
488
+ );
489
+ }
490
+ function resolveEnvironment(explicitEnvironment, brandUrls) {
491
+ if (explicitEnvironment === "staging" || explicitEnvironment === "production") {
492
+ return explicitEnvironment;
493
+ }
494
+ if (explicitEnvironment) {
495
+ return "production";
496
+ }
497
+ if (typeof window === "undefined" || !window.location) {
498
+ return "production";
499
+ }
500
+ return isFirstPartyStagingHost(window.location.hostname, brandUrls) ? "staging" : "production";
501
+ }
429
502
  function validateEnvironmentSecurity(environment, urls, logLabel = "SDK") {
430
503
  const isSecure = window.location.protocol === "https:";
431
504
  switch (environment) {
@@ -481,19 +554,19 @@ var _VerificationSDK = class _VerificationSDK {
481
554
  this.temporaryHandoffToken = null;
482
555
  this.brandUrls = brandUrls;
483
556
  this.brandConstants = brandConstants;
484
- validateConfig(config, {
485
- brandName: this.brandConstants.name,
486
- docsUrl: this.brandConstants.docsUrl
487
- });
488
- let normalizedEnvironment = config.environment || this.detectEnvironment();
489
- if (normalizedEnvironment !== "staging" && normalizedEnvironment !== "production") {
557
+ const environment = resolveEnvironment(config.environment, this.brandUrls);
558
+ if (config.environment && config.environment !== "staging" && config.environment !== "production") {
490
559
  console.warn(
491
- `${this.brandConstants.name} SDK: Unknown environment '${normalizedEnvironment}', defaulting to 'production'`
560
+ `${this.brandConstants.name} SDK: Unknown environment '${config.environment}', defaulting to 'production'`
492
561
  );
493
- normalizedEnvironment = "production";
494
562
  }
563
+ validateConfig(config, {
564
+ brandName: this.brandConstants.name,
565
+ docsUrl: this.brandConstants.docsUrl,
566
+ environment
567
+ });
495
568
  this.config = __spreadProps(__spreadValues({}, config), {
496
- environment: normalizedEnvironment,
569
+ environment,
497
570
  mode: config.mode || "redirect",
498
571
  newTabTarget: config.newTabTarget || "popup"
499
572
  });
@@ -579,11 +652,13 @@ var _VerificationSDK = class _VerificationSDK {
579
652
  * Build verification URL with HMAC-signed state
580
653
  */
581
654
  async buildVerificationUrl(options, sessionId) {
582
- var _a;
655
+ var _a, _b;
583
656
  const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
584
657
  const hasExplicitChallengeAge = options.challengeAge !== void 0;
585
658
  const hasExplicitVerificationMode = options.verificationMode !== void 0;
586
- const hasOverrides = hasExplicitChallengeAge || hasExplicitVerificationMode;
659
+ const hasExplicitFaceMatchEnabled = options.faceMatchEnabled !== void 0 || this.config.faceMatchEnabled !== void 0;
660
+ const hasOverrides = hasExplicitChallengeAge || hasExplicitVerificationMode || hasExplicitFaceMatchEnabled;
661
+ const faceMatchEnabled = (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled;
587
662
  const state = await generateState(
588
663
  {
589
664
  merchantId: this.config.apiKey,
@@ -592,6 +667,7 @@ var _VerificationSDK = class _VerificationSDK {
592
667
  cancelUrl: this.config.cancelUrl,
593
668
  challengeAge: options.challengeAge || this.config.defaultChallengeAge,
594
669
  verificationMode: options.verificationMode || this.config.defaultVerificationMode,
670
+ faceMatchEnabled,
595
671
  hasOverrides,
596
672
  // Flag to indicate explicit overrides
597
673
  externalUserId: options.externalUserId,
@@ -605,7 +681,7 @@ var _VerificationSDK = class _VerificationSDK {
605
681
  testMode: false,
606
682
  warmupPeriodMs: 500,
607
683
  qualityThreshold: 0.6,
608
- sandboxMode: (_a = this.lastSandboxMode) != null ? _a : false
684
+ sandboxMode: (_b = this.lastSandboxMode) != null ? _b : false
609
685
  },
610
686
  // Include handoffToken if available (for QR code desktop flow)
611
687
  handoffToken: this.temporaryHandoffToken || void 0,
@@ -788,16 +864,6 @@ var _VerificationSDK = class _VerificationSDK {
788
864
  };
789
865
  }
790
866
  }
791
- /**
792
- * Auto-detect environment based on current URL
793
- */
794
- detectEnvironment() {
795
- const hostname = window.location.hostname;
796
- if (hostname.includes("staging") || hostname.includes("stage")) {
797
- return "staging";
798
- }
799
- return "production";
800
- }
801
867
  /**
802
868
  * Get the current environment
803
869
  */
@@ -942,7 +1008,7 @@ var _VerificationSDK = class _VerificationSDK {
942
1008
  * Create session internally for public keys
943
1009
  */
944
1010
  async createInternalSession(options) {
945
- var _a, _b;
1011
+ var _a, _b, _c;
946
1012
  try {
947
1013
  const portalApiUrl = this.getPortalApiUrl();
948
1014
  const response = await fetch(`${portalApiUrl}/api/v1/sessions/create`, {
@@ -957,6 +1023,7 @@ var _VerificationSDK = class _VerificationSDK {
957
1023
  cancelUrl: this.config.cancelUrl,
958
1024
  challengeAge: options.challengeAge,
959
1025
  verificationMode: options.verificationMode,
1026
+ faceMatchEnabled: (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled,
960
1027
  merchantName: document.title || window.location.hostname,
961
1028
  externalUserId: options.externalUserId
962
1029
  })
@@ -1004,7 +1071,7 @@ var _VerificationSDK = class _VerificationSDK {
1004
1071
  environment: this.config.environment,
1005
1072
  apiKeyType: "public"
1006
1073
  }, this.brandConstants.name);
1007
- (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
1074
+ (_c = (_b = this.config).onError) == null ? void 0 : _c.call(_b, error);
1008
1075
  throw new Error(`Failed to create verification session: ${errorMessage}`);
1009
1076
  }
1010
1077
  }
@@ -1120,14 +1187,14 @@ var BRAND_URLS = {
1120
1187
  ]
1121
1188
  },
1122
1189
  staging: {
1123
- apiUrl: "https://api.verityav-staging-usw1a.privateav.com",
1124
- verifyUiUrl: "https://verify.verityav-staging-usw1a.privateav.com",
1125
- engineUrl: "https://engine.verityav-staging-usw1a.privateav.com",
1126
- wsUrl: "wss://engine.verityav-staging-usw1a.privateav.com/api/websocket/stream",
1190
+ apiUrl: "https://api.staging.privateav.com",
1191
+ verifyUiUrl: "https://verify.staging.privateav.com",
1192
+ engineUrl: "https://engine.staging.privateav.com",
1193
+ wsUrl: "wss://engine.staging.privateav.com/api/websocket/stream",
1127
1194
  trustedOrigins: [
1128
- "https://verify.verityav-staging-usw1a.privateav.com",
1129
- "https://portal.verityav-staging-usw1a.privateav.com",
1130
- "https://api.verityav-staging-usw1a.privateav.com"
1195
+ "https://verify.staging.privateav.com",
1196
+ "https://portal.staging.privateav.com",
1197
+ "https://api.staging.privateav.com"
1131
1198
  ]
1132
1199
  }
1133
1200
  };
@@ -1147,7 +1214,7 @@ var PrivateAV = class extends VerificationSDK {
1147
1214
  super(config, BRAND_URLS, BRAND_CONSTANTS);
1148
1215
  }
1149
1216
  };
1150
- var VERSION = "3.5.2";
1217
+ var VERSION = "3.5.5";
1151
1218
  PrivateAV.VERSION = VERSION;
1152
1219
  if (typeof window !== "undefined") {
1153
1220
  setupPolyfills();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privateav/sdk",
3
- "version": "3.5.2",
3
+ "version": "3.5.5",
4
4
  "description": "PrivateAV SDK - Lightweight redirect-based age verification",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/privateav.min.js CHANGED
@@ -1,3 +1,3 @@
1
- /* PrivateAV SDK v3.5.2 */
2
- "use strict";var PrivateAVSDK=(()=>{var S=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?S(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,f=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(y)for(var t of y(e))W.call(e,t)&&B(n,t,e[t]);return n},U=(n,e)=>de(n,pe(e));var H=(n,e)=>{var t={};for(var r in n)T.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&y)for(var r of y(n))e.indexOf(r)<0&&W.call(n,r)&&(t[r]=n[r]);return t};var A=(n,e)=>()=>(n&&(e=n(n=0)),e);var F=(n,e)=>{for(var t in e)S(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!T.call(n,i)&&i!==t&&S(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(S({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var o;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(c=>{if(c.startsWith("*.")){let l=c.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===c})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(o=n.data)==null?void 0:o.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let o of i)if(o.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function g(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,f(f({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),o=(this.attempts.get(e)||[]).filter(a=>r-a<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(o.push(r),this.attempts.set(e,o),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>we,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function V(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(a)).map(c=>c.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await V(n,t);return ve(e,r)}catch(r){return!1}}function ve(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function we(n,e){let t=U(f({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await V(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let o=atob(n),a=JSON.parse(o);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:c,signature:l}=a,d=JSON.stringify(c);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(c.timestamp){let h=Date.now()-c.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=c,{timestamp:p,nonce:u}=i;return H(i,["timestamp","nonce"])}catch(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var ee=A(()=>{"use strict";L()});function oe(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>E)throw new Error(`returnUrl exceeds maximum length of ${E} characters`);let t=Ue(),r=x(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>E)throw new Error(`cancelUrl exceeds maximum length of ${E} characters`);let i=x(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function se(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,E,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,E=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ce={};F(Ce,{PrivateAV:()=>m,VERSION:()=>ce,default:()=>be});function q(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).map(t=>t.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${n} Browser Compatibility:`,e.join("; "))}L();function b(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{b(n,e),Ee(n,e)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}R();var I=class I{constructor(e,t,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=t,this.brandConstants=r,oe(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=U(f({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),g("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,o,a,c,l,d;let t=this.isPublicKey(),r;if(t)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let s=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw g("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(c=(a=this.config).onError)==null||c.call(a,s),s}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let s=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(s,this.brandConstants.name)){let u=new Error("Too many verification attempts. Please wait before trying again.");throw g("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,u),u}let p=await this.buildVerificationUrl(e,r);g("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(p,r):(this.unlockVerification(),this.redirect(p))}catch(s){throw this.unlockVerification(),s}}async buildVerificationUrl(e,t){var s;let r=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,o=e.verificationMode!==void 0,a=i||o,c=await se({merchantId:this.config.apiKey,sessionId:t,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,hasOverrides:a,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(s=this.lastSandboxMode)!=null?s:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),l=e.language||this.config.language;if(this.lastVerifyUrl)try{let p=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(p);return u.searchParams.set("state",c),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),l&&u.searchParams.set("lang",l),u.toString()}catch(p){}let d=new URLSearchParams({state:c,sessionId:t,mode:this.config.mode});return e.skipIntro&&d.set("skip_intro","true"),e.autoReturn&&d.set("auto_return","true"),l&&d.set("lang",l),`${r}/?${d.toString()}`}redirect(e){window.location.href=e}openNewTab(e,t){var l,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(l=this.config).onError)==null||d.call(l,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),o=this.getAllowedCustomOrigins(e),a=this.brandConstants.messageType,c=this.brandConstants.legacyMessageType;this.messageListener=s=>{var M,D,_,O,$,N,K;let p=(M=s.data)==null?void 0:M.type;if(!p||typeof p!="string"||!(c?[a,c]:[a]).includes(p))return;if(!G(s,i,o,this.brandConstants.name)){g("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=s.data)==null?void 0:D.type},this.brandConstants.name);return}let v=J(s,t,a,c);if(!v.isValid){g("POSTMESSAGE_VALIDATION_FAILED",{error:v.error,origin:s.origin,sessionId:t.substring(0,8)+"...",messageType:(_=s.data)==null?void 0:_.type},this.brandConstants.name);return}let h=s.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:s.data.sessionId,status:h,timestamp:s.data.timestamp,externalUserId:s.data.externalUserId};this.hasReceivedResult=!0,g("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:s.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(g("POPUP_CLOSED_BY_USER",{sessionId:t.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(t,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{g("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...t)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,t))}}detectEnvironment(){let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,g("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let t=e.closePopup!==!1;this.popupWindow&&(t&&!this.popupWindow.closed&&this.popupWindow.close(),(t||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,t){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,g("VERIFICATION_CANCELLED",{source:t,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){g("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(t){g("CANCEL_REDIRECT_FAILED",{error:t instanceof Error?t.message:String(t),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){g("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var t,r;try{let i=this.getPortalApiUrl(),o=await fetch(`${i}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!o.ok){let l=await o.json().catch(()=>({})),d=l==null?void 0:l.code;if(this.isBillingBlockError(d)){let s=e.language||this.config.language;this.openBillingBlockPage(d,l==null?void 0:l.portalUrl,s)}throw new Error(`Failed to create session: ${o.status} ${o.statusText}. ${l.message||""}`)}let a=await o.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,g("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(i){let o=i instanceof Error?i.message:String(i);throw g("INTERNAL_SESSION_FAILED",{error:o,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),new Error(`Failed to create verification session: ${o}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,t,r){var i,o,a,c;try{let l=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(l),s=new URL(d);s.searchParams.set("blocked",e),t&&s.searchParams.set("portalUrl",t);let p=r||this.config.language;if(p&&s.searchParams.set("lang",p),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(s.toString(),"_blank"):window.open(s.toString(),this.brandConstants.popupName,"width=600,height=700"))||(o=(i=this.config).onError)==null||o.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(s.toString())}catch(l){let d=l instanceof Error?l.message:String(l);(c=(a=this.config).onError)==null||c.call(a,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(I.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var le={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.privateav.com",verifyUiUrl:"https://verify.verityav-staging-usw1a.privateav.com",engineUrl:"https://engine.verityav-staging-usw1a.privateav.com",wsUrl:"wss://engine.verityav-staging-usw1a.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.verityav-staging-usw1a.privateav.com","https://portal.verityav-staging-usw1a.privateav.com","https://api.verityav-staging-usw1a.privateav.com"]}},k={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var m=class extends C{constructor(e){super(e,le,k)}},ce="3.5.2";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
1
+ /* PrivateAV SDK v3.5.5 */
2
+ "use strict";var PrivateAVSDK=(()=>{var S=Object.defineProperty,pe=Object.defineProperties,ue=Object.getOwnPropertyDescriptor,fe=Object.getOwnPropertyDescriptors,he=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(t,e,n)=>e in t?S(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,w=(t,e)=>{for(var n in e||(e={}))T.call(e,n)&&B(t,n,e[n]);if(y)for(var n of y(e))W.call(e,n)&&B(t,n,e[n]);return t},U=(t,e)=>pe(t,fe(e));var H=(t,e)=>{var n={};for(var r in t)T.call(t,r)&&e.indexOf(r)<0&&(n[r]=t[r]);if(t!=null&&y)for(var r of y(t))e.indexOf(r)<0&&W.call(t,r)&&(n[r]=t[r]);return n};var A=(t,e,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};var F=(t,e)=>{for(var n in e)S(t,n,{get:e[n],enumerable:!0})},me=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of he(e))!T.call(t,i)&&i!==n&&S(t,i,{get:()=>e[i],enumerable:!(r=ue(e,i))||r.enumerable});return t};var we=t=>me(S({},"__esModule",{value:!0}),t);function ve(t,e){return e.includes(t)}function G(t,e,n=[],r="SDK"){var o;let{origin:i}=t;return ve(i,e)||n.length>0&&n.some(a=>{if(a.startsWith("*.")){let c=a.slice(2);return i.endsWith(`.${c}`)||i===`https://${c}`||i===`http://${c}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:n,eventType:(o=t.data)==null?void 0:o.type}),!1)}function J(t,e,n,r){let{data:i}=t;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[n,r]:[n]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(t,e="SDK"){t==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(t,e,n="SDK"){try{let r=new URL(t);if(r.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let o of i)if(o.test(t))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(t,e,n="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${n} Security Event: ${t}`,w(w({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,n="SDK"){let r=Date.now(),o=(this.attempts.get(e)||[]).filter(l=>r-l<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${e}`),!1):(o.push(r),this.attempts.set(e,o),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>Se,generateHMAC:()=>M,generateSecureToken:()=>z,parseSignedState:()=>Ue,verifyHMAC:()=>Z});async function M(t,e){let n=new TextEncoder,r=n.encode(e),i=n.encode(t),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(l)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function Z(t,e,n){try{let r=await M(t,n);return ye(e,r)}catch(r){return!1}}function ye(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function z(t=32){let e=new Uint8Array(t);return crypto.getRandomValues(e),Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}async function Se(t,e){let n=U(w({},t),{timestamp:Date.now(),nonce:z(16)}),r=JSON.stringify(n),i=await M(r,e);return btoa(JSON.stringify({data:n,signature:i}))}async function Ue(t,e,n=te,r="SDK"){try{let o=atob(t),l=JSON.parse(o);if(!l.data||!l.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:c}=l,d=JSON.stringify(a);if(!await Z(d,c,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>n)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:n}),null}let i=a,{timestamp:g,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var ee=A(()=>{"use strict";V()});function oe(t,e){var i;if(!t.apiKey)throw new Error("apiKey is required");if(t.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!be.test(t.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&t.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!t.returnUrl)throw new Error("returnUrl is required");if(t.returnUrl.length>b)throw new Error(`returnUrl exceeds maximum length of ${b} characters`);let n=(i=e.environment)!=null?i:"production",r=x(t.returnUrl,n,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(t.cancelUrl){if(t.cancelUrl.length>b)throw new Error(`cancelUrl exceeds maximum length of ${b} characters`);let o=x(t.cancelUrl,n,e.brandName);if(!o.isValid)throw new Error(`cancelUrl validation failed: ${o.error}`)}if(t.defaultChallengeAge!==void 0){if(t.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(t.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(t.defaultVerificationMode&&!["L1","L2"].includes(t.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(t.faceMatchEnabled!==void 0&&typeof t.faceMatchEnabled!="boolean")throw new Error("faceMatchEnabled must be a boolean");if(t.mode&&!["redirect","new-tab"].includes(t.mode))throw new Error("mode must be redirect or new-tab");if(t.newTabTarget&&!["popup","tab"].includes(t.newTabTarget))throw new Error("newTabTarget must be popup or tab")}async function se(t,e,n,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(t,n)}var ne,re,b,ie,te,be,V=A(()=>{"use strict";R();ne=25,re=150,b=2048,ie=128,te=6e5,be=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var xe={};F(xe,{PrivateAV:()=>v,VERSION:()=>ge,default:()=>Pe});function j(){crypto.randomUUID||(crypto.randomUUID=function(){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t).map(n=>n.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function q(t="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${t} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${t} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${t} Browser Compatibility:`,e.join("; "))}V();function E(t,e){let n=e.verifyUiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for ${t} environment`);return n}function Ee(t,e){let n=e.apiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${t} environment`);return n}function Ce(t){if(!t)return null;try{return new URL(t).hostname.toLowerCase()}catch(e){return null}}function Ie(t){let e=t.split(".");return e.length<=2?t:e.slice(1).join(".")}function ae(t){let e=new Set;for(let n of t){let r=Ce(n);r&&e.add(Ie(r))}return e}function Te(t){var o,l;if(!t||!t.staging)return[];let e=t.staging,n=ae([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(o=e.trustedOrigins)!=null?o:[]]),r=t.production,i=r?ae([r.apiUrl,r.verifyUiUrl,r.engineUrl,r.wsUrl,...(l=r.trustedOrigins)!=null?l:[]]):new Set;for(let a of i)n.delete(a);return Array.from(n)}function Ae(t,e){let n=(t||"").toLowerCase();return n?Te(e).some(r=>n===r||n.endsWith(`.${r}`)):!1}function le(t,e){return t==="staging"||t==="production"?t:t||typeof window=="undefined"||!window.location?"production":Ae(window.location.hostname,e)?"staging":"production"}function ce(t,e,n="SDK"){let r=window.location.protocol==="https:";switch(t){case"production":r||console.warn(`${n} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${n} Warning: HTTPS strongly recommended in staging environment`);break}try{E(t,e),Ee(t,e)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}R();var I=class I{constructor(e,n,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=n,this.brandConstants=r;let i=le(e.environment,this.brandUrls);e.environment&&e.environment!=="staging"&&e.environment!=="production"&&console.warn(`${this.brandConstants.name} SDK: Unknown environment '${e.environment}', defaulting to 'production'`),oe(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:i}),this.config=U(w({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ce(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,o,l,a,c,d;let n=this.isPublicKey(),r;if(n)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let s=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(l=this.config).onError)==null||a.call(l,s),s}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let s=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(s,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(c=this.config).onError)==null||d.call(c,f),f}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(s){throw this.unlockVerification(),s}}async buildVerificationUrl(e,n){var f,m;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,o=e.verificationMode!==void 0,l=e.faceMatchEnabled!==void 0||this.config.faceMatchEnabled!==void 0,a=i||o||l,c=(f=e.faceMatchEnabled)!=null?f:this.config.faceMatchEnabled,d=await se({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,faceMatchEnabled:c,hasOverrides:a,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(m=this.lastSandboxMode)!=null?m:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),s=e.language||this.config.language;if(this.lastVerifyUrl)try{let h=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(h);return u.searchParams.set("state",d),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),s&&u.searchParams.set("lang",s),u.toString()}catch(h){}let g=new URLSearchParams({state:d,sessionId:n,mode:this.config.mode});return e.skipIntro&&g.set("skip_intro","true"),e.autoReturn&&g.set("auto_return","true"),s&&g.set("lang",s),`${r}/?${g.toString()}`}redirect(e){window.location.href=e}openNewTab(e,n){var c,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(c=this.config).onError)==null||d.call(c,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),o=this.getAllowedCustomOrigins(e),l=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=s=>{var k,D,_,O,$,N,K;let g=(k=s.data)==null?void 0:k.type;if(!g||typeof g!="string"||!(a?[l,a]:[l]).includes(g))return;if(!G(s,i,o,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=s.data)==null?void 0:D.type},this.brandConstants.name);return}let m=J(s,n,l,a);if(!m.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:m.error,origin:s.origin,sessionId:n.substring(0,8)+"...",messageType:(_=s.data)==null?void 0:_.type},this.brandConstants.name);return}let h=s.data.status;if(h==="cancelled"){this.handleCancellation(n,"postmessage");return}let u={sessionId:s.data.sessionId,status:h,timestamp:s.data.timestamp,externalUserId:s.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:u.status,sessionId:n.substring(0,8)+"...",origin:s.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),u.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,u):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${u.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(p("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(n,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...n)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,n))}}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,p("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let n=e.closePopup!==!1;this.popupWindow&&(n&&!this.popupWindow.closed&&this.popupWindow.close(),(n||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,n){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,p("VERIFICATION_CANCELLED",{source:n,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let n=decodeURIComponent(this.config.cancelUrl),r=new URL(n);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(n){p("CANCEL_REDIRECT_FAILED",{error:n instanceof Error?n.message:String(n),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var n,r,i;try{let o=this.getPortalApiUrl(),l=await fetch(`${o}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,faceMatchEnabled:(n=e.faceMatchEnabled)!=null?n:this.config.faceMatchEnabled,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!l.ok){let d=await l.json().catch(()=>({})),s=d==null?void 0:d.code;if(this.isBillingBlockError(s)){let g=e.language||this.config.language;this.openBillingBlockPage(s,d==null?void 0:d.portalUrl,g)}throw new Error(`Failed to create session: ${l.status} ${l.statusText}. ${d.message||""}`)}let a=await l.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(o){let l=o instanceof Error?o.message:String(o);throw p("INTERNAL_SESSION_FAILED",{error:l,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(i=(r=this.config).onError)==null||i.call(r,o),new Error(`Failed to create verification session: ${l}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,n,r){var i,o,l,a;try{let c=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(c),s=new URL(d);s.searchParams.set("blocked",e),n&&s.searchParams.set("portalUrl",n);let g=r||this.config.language;if(g&&s.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(s.toString(),"_blank"):window.open(s.toString(),this.brandConstants.popupName,"width=600,height=700"))||(o=(i=this.config).onError)==null||o.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(s.toString())}catch(c){let d=c instanceof Error?c.message:String(c);(a=(l=this.config).onError)==null||a.call(l,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let n=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&n.add(r),i&&n.add(i),Array.from(n)}getLocalOrigin(e){if(!e)return null;try{let n=new URL(e);if(I.LOCAL_HOSTNAMES.has(n.hostname))return n.origin}catch(n){return null}return null}applyLocalVerifyOverride(e){let n=this.getLocalOrigin(this.config.verifyUrl||null);if(!n)return e;try{let r=new URL(n),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var de={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.staging.privateav.com",verifyUiUrl:"https://verify.staging.privateav.com",engineUrl:"https://engine.staging.privateav.com",wsUrl:"wss://engine.staging.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.staging.privateav.com","https://portal.staging.privateav.com","https://api.staging.privateav.com"]}},L={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var v=class extends C{constructor(e){super(e,de,L)}},ge="3.5.5";v.VERSION=ge;typeof window!="undefined"&&(j(),q(`${L.name} SDK`));var Pe=v;return we(xe);})();
3
3
  if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
package/sdk.min.js CHANGED
@@ -1,3 +1,3 @@
1
- /* PrivateAV SDK v3.5.2 */
2
- "use strict";var PrivateAVSDK=(()=>{var S=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?S(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,f=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(y)for(var t of y(e))W.call(e,t)&&B(n,t,e[t]);return n},U=(n,e)=>de(n,pe(e));var H=(n,e)=>{var t={};for(var r in n)T.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&y)for(var r of y(n))e.indexOf(r)<0&&W.call(n,r)&&(t[r]=n[r]);return t};var A=(n,e)=>()=>(n&&(e=n(n=0)),e);var F=(n,e)=>{for(var t in e)S(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!T.call(n,i)&&i!==t&&S(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(S({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var o;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(c=>{if(c.startsWith("*.")){let l=c.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===c})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(o=n.data)==null?void 0:o.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let o of i)if(o.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function g(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,f(f({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),o=(this.attempts.get(e)||[]).filter(a=>r-a<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(o.push(r),this.attempts.set(e,o),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>we,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function V(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(a)).map(c=>c.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await V(n,t);return ve(e,r)}catch(r){return!1}}function ve(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function we(n,e){let t=U(f({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await V(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let o=atob(n),a=JSON.parse(o);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:c,signature:l}=a,d=JSON.stringify(c);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(c.timestamp){let h=Date.now()-c.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=c,{timestamp:p,nonce:u}=i;return H(i,["timestamp","nonce"])}catch(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var ee=A(()=>{"use strict";L()});function oe(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>E)throw new Error(`returnUrl exceeds maximum length of ${E} characters`);let t=Ue(),r=x(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>E)throw new Error(`cancelUrl exceeds maximum length of ${E} characters`);let i=x(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function se(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,E,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,E=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ce={};F(Ce,{PrivateAV:()=>m,VERSION:()=>ce,default:()=>be});function q(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).map(t=>t.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${n} Browser Compatibility:`,e.join("; "))}L();function b(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{b(n,e),Ee(n,e)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}R();var I=class I{constructor(e,t,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=t,this.brandConstants=r,oe(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=U(f({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),g("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,o,a,c,l,d;let t=this.isPublicKey(),r;if(t)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let s=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw g("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(c=(a=this.config).onError)==null||c.call(a,s),s}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let s=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(s,this.brandConstants.name)){let u=new Error("Too many verification attempts. Please wait before trying again.");throw g("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,u),u}let p=await this.buildVerificationUrl(e,r);g("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(p,r):(this.unlockVerification(),this.redirect(p))}catch(s){throw this.unlockVerification(),s}}async buildVerificationUrl(e,t){var s;let r=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,o=e.verificationMode!==void 0,a=i||o,c=await se({merchantId:this.config.apiKey,sessionId:t,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,hasOverrides:a,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(s=this.lastSandboxMode)!=null?s:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),l=e.language||this.config.language;if(this.lastVerifyUrl)try{let p=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(p);return u.searchParams.set("state",c),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),l&&u.searchParams.set("lang",l),u.toString()}catch(p){}let d=new URLSearchParams({state:c,sessionId:t,mode:this.config.mode});return e.skipIntro&&d.set("skip_intro","true"),e.autoReturn&&d.set("auto_return","true"),l&&d.set("lang",l),`${r}/?${d.toString()}`}redirect(e){window.location.href=e}openNewTab(e,t){var l,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(l=this.config).onError)==null||d.call(l,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),o=this.getAllowedCustomOrigins(e),a=this.brandConstants.messageType,c=this.brandConstants.legacyMessageType;this.messageListener=s=>{var M,D,_,O,$,N,K;let p=(M=s.data)==null?void 0:M.type;if(!p||typeof p!="string"||!(c?[a,c]:[a]).includes(p))return;if(!G(s,i,o,this.brandConstants.name)){g("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=s.data)==null?void 0:D.type},this.brandConstants.name);return}let v=J(s,t,a,c);if(!v.isValid){g("POSTMESSAGE_VALIDATION_FAILED",{error:v.error,origin:s.origin,sessionId:t.substring(0,8)+"...",messageType:(_=s.data)==null?void 0:_.type},this.brandConstants.name);return}let h=s.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:s.data.sessionId,status:h,timestamp:s.data.timestamp,externalUserId:s.data.externalUserId};this.hasReceivedResult=!0,g("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:s.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(g("POPUP_CLOSED_BY_USER",{sessionId:t.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(t,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{g("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...t)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,t))}}detectEnvironment(){let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,g("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let t=e.closePopup!==!1;this.popupWindow&&(t&&!this.popupWindow.closed&&this.popupWindow.close(),(t||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,t){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,g("VERIFICATION_CANCELLED",{source:t,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){g("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(t){g("CANCEL_REDIRECT_FAILED",{error:t instanceof Error?t.message:String(t),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){g("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var t,r;try{let i=this.getPortalApiUrl(),o=await fetch(`${i}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!o.ok){let l=await o.json().catch(()=>({})),d=l==null?void 0:l.code;if(this.isBillingBlockError(d)){let s=e.language||this.config.language;this.openBillingBlockPage(d,l==null?void 0:l.portalUrl,s)}throw new Error(`Failed to create session: ${o.status} ${o.statusText}. ${l.message||""}`)}let a=await o.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,g("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(i){let o=i instanceof Error?i.message:String(i);throw g("INTERNAL_SESSION_FAILED",{error:o,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),new Error(`Failed to create verification session: ${o}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,t,r){var i,o,a,c;try{let l=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(l),s=new URL(d);s.searchParams.set("blocked",e),t&&s.searchParams.set("portalUrl",t);let p=r||this.config.language;if(p&&s.searchParams.set("lang",p),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(s.toString(),"_blank"):window.open(s.toString(),this.brandConstants.popupName,"width=600,height=700"))||(o=(i=this.config).onError)==null||o.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(s.toString())}catch(l){let d=l instanceof Error?l.message:String(l);(c=(a=this.config).onError)==null||c.call(a,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(I.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var le={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.privateav.com",verifyUiUrl:"https://verify.verityav-staging-usw1a.privateav.com",engineUrl:"https://engine.verityav-staging-usw1a.privateav.com",wsUrl:"wss://engine.verityav-staging-usw1a.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.verityav-staging-usw1a.privateav.com","https://portal.verityav-staging-usw1a.privateav.com","https://api.verityav-staging-usw1a.privateav.com"]}},k={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var m=class extends C{constructor(e){super(e,le,k)}},ce="3.5.2";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
1
+ /* PrivateAV SDK v3.5.5 */
2
+ "use strict";var PrivateAVSDK=(()=>{var S=Object.defineProperty,pe=Object.defineProperties,ue=Object.getOwnPropertyDescriptor,fe=Object.getOwnPropertyDescriptors,he=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(t,e,n)=>e in t?S(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,w=(t,e)=>{for(var n in e||(e={}))T.call(e,n)&&B(t,n,e[n]);if(y)for(var n of y(e))W.call(e,n)&&B(t,n,e[n]);return t},U=(t,e)=>pe(t,fe(e));var H=(t,e)=>{var n={};for(var r in t)T.call(t,r)&&e.indexOf(r)<0&&(n[r]=t[r]);if(t!=null&&y)for(var r of y(t))e.indexOf(r)<0&&W.call(t,r)&&(n[r]=t[r]);return n};var A=(t,e,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};var F=(t,e)=>{for(var n in e)S(t,n,{get:e[n],enumerable:!0})},me=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of he(e))!T.call(t,i)&&i!==n&&S(t,i,{get:()=>e[i],enumerable:!(r=ue(e,i))||r.enumerable});return t};var we=t=>me(S({},"__esModule",{value:!0}),t);function ve(t,e){return e.includes(t)}function G(t,e,n=[],r="SDK"){var o;let{origin:i}=t;return ve(i,e)||n.length>0&&n.some(a=>{if(a.startsWith("*.")){let c=a.slice(2);return i.endsWith(`.${c}`)||i===`https://${c}`||i===`http://${c}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:n,eventType:(o=t.data)==null?void 0:o.type}),!1)}function J(t,e,n,r){let{data:i}=t;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[n,r]:[n]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(t,e="SDK"){t==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(t,e,n="SDK"){try{let r=new URL(t);if(r.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let o of i)if(o.test(t))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(t,e,n="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${n} Security Event: ${t}`,w(w({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,n="SDK"){let r=Date.now(),o=(this.attempts.get(e)||[]).filter(l=>r-l<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${e}`),!1):(o.push(r),this.attempts.set(e,o),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>Se,generateHMAC:()=>M,generateSecureToken:()=>z,parseSignedState:()=>Ue,verifyHMAC:()=>Z});async function M(t,e){let n=new TextEncoder,r=n.encode(e),i=n.encode(t),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(l)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function Z(t,e,n){try{let r=await M(t,n);return ye(e,r)}catch(r){return!1}}function ye(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function z(t=32){let e=new Uint8Array(t);return crypto.getRandomValues(e),Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}async function Se(t,e){let n=U(w({},t),{timestamp:Date.now(),nonce:z(16)}),r=JSON.stringify(n),i=await M(r,e);return btoa(JSON.stringify({data:n,signature:i}))}async function Ue(t,e,n=te,r="SDK"){try{let o=atob(t),l=JSON.parse(o);if(!l.data||!l.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:c}=l,d=JSON.stringify(a);if(!await Z(d,c,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>n)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:n}),null}let i=a,{timestamp:g,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var ee=A(()=>{"use strict";V()});function oe(t,e){var i;if(!t.apiKey)throw new Error("apiKey is required");if(t.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!be.test(t.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&t.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!t.returnUrl)throw new Error("returnUrl is required");if(t.returnUrl.length>b)throw new Error(`returnUrl exceeds maximum length of ${b} characters`);let n=(i=e.environment)!=null?i:"production",r=x(t.returnUrl,n,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(t.cancelUrl){if(t.cancelUrl.length>b)throw new Error(`cancelUrl exceeds maximum length of ${b} characters`);let o=x(t.cancelUrl,n,e.brandName);if(!o.isValid)throw new Error(`cancelUrl validation failed: ${o.error}`)}if(t.defaultChallengeAge!==void 0){if(t.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(t.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(t.defaultVerificationMode&&!["L1","L2"].includes(t.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(t.faceMatchEnabled!==void 0&&typeof t.faceMatchEnabled!="boolean")throw new Error("faceMatchEnabled must be a boolean");if(t.mode&&!["redirect","new-tab"].includes(t.mode))throw new Error("mode must be redirect or new-tab");if(t.newTabTarget&&!["popup","tab"].includes(t.newTabTarget))throw new Error("newTabTarget must be popup or tab")}async function se(t,e,n,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(t,n)}var ne,re,b,ie,te,be,V=A(()=>{"use strict";R();ne=25,re=150,b=2048,ie=128,te=6e5,be=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var xe={};F(xe,{PrivateAV:()=>v,VERSION:()=>ge,default:()=>Pe});function j(){crypto.randomUUID||(crypto.randomUUID=function(){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t).map(n=>n.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function q(t="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${t} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${t} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${t} Browser Compatibility:`,e.join("; "))}V();function E(t,e){let n=e.verifyUiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for ${t} environment`);return n}function Ee(t,e){let n=e.apiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${t} environment`);return n}function Ce(t){if(!t)return null;try{return new URL(t).hostname.toLowerCase()}catch(e){return null}}function Ie(t){let e=t.split(".");return e.length<=2?t:e.slice(1).join(".")}function ae(t){let e=new Set;for(let n of t){let r=Ce(n);r&&e.add(Ie(r))}return e}function Te(t){var o,l;if(!t||!t.staging)return[];let e=t.staging,n=ae([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(o=e.trustedOrigins)!=null?o:[]]),r=t.production,i=r?ae([r.apiUrl,r.verifyUiUrl,r.engineUrl,r.wsUrl,...(l=r.trustedOrigins)!=null?l:[]]):new Set;for(let a of i)n.delete(a);return Array.from(n)}function Ae(t,e){let n=(t||"").toLowerCase();return n?Te(e).some(r=>n===r||n.endsWith(`.${r}`)):!1}function le(t,e){return t==="staging"||t==="production"?t:t||typeof window=="undefined"||!window.location?"production":Ae(window.location.hostname,e)?"staging":"production"}function ce(t,e,n="SDK"){let r=window.location.protocol==="https:";switch(t){case"production":r||console.warn(`${n} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${n} Warning: HTTPS strongly recommended in staging environment`);break}try{E(t,e),Ee(t,e)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}R();var I=class I{constructor(e,n,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=n,this.brandConstants=r;let i=le(e.environment,this.brandUrls);e.environment&&e.environment!=="staging"&&e.environment!=="production"&&console.warn(`${this.brandConstants.name} SDK: Unknown environment '${e.environment}', defaulting to 'production'`),oe(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:i}),this.config=U(w({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ce(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,o,l,a,c,d;let n=this.isPublicKey(),r;if(n)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let s=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(l=this.config).onError)==null||a.call(l,s),s}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let s=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(s,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(c=this.config).onError)==null||d.call(c,f),f}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(s){throw this.unlockVerification(),s}}async buildVerificationUrl(e,n){var f,m;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,o=e.verificationMode!==void 0,l=e.faceMatchEnabled!==void 0||this.config.faceMatchEnabled!==void 0,a=i||o||l,c=(f=e.faceMatchEnabled)!=null?f:this.config.faceMatchEnabled,d=await se({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,faceMatchEnabled:c,hasOverrides:a,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(m=this.lastSandboxMode)!=null?m:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),s=e.language||this.config.language;if(this.lastVerifyUrl)try{let h=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(h);return u.searchParams.set("state",d),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),s&&u.searchParams.set("lang",s),u.toString()}catch(h){}let g=new URLSearchParams({state:d,sessionId:n,mode:this.config.mode});return e.skipIntro&&g.set("skip_intro","true"),e.autoReturn&&g.set("auto_return","true"),s&&g.set("lang",s),`${r}/?${g.toString()}`}redirect(e){window.location.href=e}openNewTab(e,n){var c,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(c=this.config).onError)==null||d.call(c,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),o=this.getAllowedCustomOrigins(e),l=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=s=>{var k,D,_,O,$,N,K;let g=(k=s.data)==null?void 0:k.type;if(!g||typeof g!="string"||!(a?[l,a]:[l]).includes(g))return;if(!G(s,i,o,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=s.data)==null?void 0:D.type},this.brandConstants.name);return}let m=J(s,n,l,a);if(!m.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:m.error,origin:s.origin,sessionId:n.substring(0,8)+"...",messageType:(_=s.data)==null?void 0:_.type},this.brandConstants.name);return}let h=s.data.status;if(h==="cancelled"){this.handleCancellation(n,"postmessage");return}let u={sessionId:s.data.sessionId,status:h,timestamp:s.data.timestamp,externalUserId:s.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:u.status,sessionId:n.substring(0,8)+"...",origin:s.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),u.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,u):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${u.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(p("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(n,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...n)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,n))}}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,p("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let n=e.closePopup!==!1;this.popupWindow&&(n&&!this.popupWindow.closed&&this.popupWindow.close(),(n||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,n){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,p("VERIFICATION_CANCELLED",{source:n,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let n=decodeURIComponent(this.config.cancelUrl),r=new URL(n);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(n){p("CANCEL_REDIRECT_FAILED",{error:n instanceof Error?n.message:String(n),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var n,r,i;try{let o=this.getPortalApiUrl(),l=await fetch(`${o}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,faceMatchEnabled:(n=e.faceMatchEnabled)!=null?n:this.config.faceMatchEnabled,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!l.ok){let d=await l.json().catch(()=>({})),s=d==null?void 0:d.code;if(this.isBillingBlockError(s)){let g=e.language||this.config.language;this.openBillingBlockPage(s,d==null?void 0:d.portalUrl,g)}throw new Error(`Failed to create session: ${l.status} ${l.statusText}. ${d.message||""}`)}let a=await l.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(o){let l=o instanceof Error?o.message:String(o);throw p("INTERNAL_SESSION_FAILED",{error:l,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(i=(r=this.config).onError)==null||i.call(r,o),new Error(`Failed to create verification session: ${l}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,n,r){var i,o,l,a;try{let c=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(c),s=new URL(d);s.searchParams.set("blocked",e),n&&s.searchParams.set("portalUrl",n);let g=r||this.config.language;if(g&&s.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(s.toString(),"_blank"):window.open(s.toString(),this.brandConstants.popupName,"width=600,height=700"))||(o=(i=this.config).onError)==null||o.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(s.toString())}catch(c){let d=c instanceof Error?c.message:String(c);(a=(l=this.config).onError)==null||a.call(l,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let n=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&n.add(r),i&&n.add(i),Array.from(n)}getLocalOrigin(e){if(!e)return null;try{let n=new URL(e);if(I.LOCAL_HOSTNAMES.has(n.hostname))return n.origin}catch(n){return null}return null}applyLocalVerifyOverride(e){let n=this.getLocalOrigin(this.config.verifyUrl||null);if(!n)return e;try{let r=new URL(n),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var de={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.staging.privateav.com",verifyUiUrl:"https://verify.staging.privateav.com",engineUrl:"https://engine.staging.privateav.com",wsUrl:"wss://engine.staging.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.staging.privateav.com","https://portal.staging.privateav.com","https://api.staging.privateav.com"]}},L={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var v=class extends C{constructor(e){super(e,de,L)}},ge="3.5.5";v.VERSION=ge;typeof window!="undefined"&&(j(),q(`${L.name} SDK`));var Pe=v;return we(xe);})();
3
3
  if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
package/types/base.d.ts CHANGED
@@ -41,6 +41,11 @@ export interface SDKConfig {
41
41
  * Can be overridden per verification
42
42
  */
43
43
  defaultVerificationMode?: 'L1' | 'L2';
44
+ /**
45
+ * Default face match setting for verification sessions
46
+ * @default true
47
+ */
48
+ faceMatchEnabled?: boolean;
44
49
  /**
45
50
  * Override API URL
46
51
  */
@@ -60,7 +65,7 @@ export interface SDKConfig {
60
65
  onCancel?: () => boolean | void;
61
66
  /**
62
67
  * Default UI language for verification screens
63
- * Supported: 'en', 'de', 'es', 'fr'
68
+ * Supported: 'en', 'de', 'es', 'fr', 'pt', 'it'
64
69
  * Can be overridden per verification call
65
70
  */
66
71
  language?: string;
@@ -82,6 +87,11 @@ export interface VerificationOptions {
82
87
  * @default Uses merchant dashboard configuration
83
88
  */
84
89
  verificationMode?: 'L1' | 'L2';
90
+ /**
91
+ * Whether biometric selfie-vs-ID face matching is enabled for this verification
92
+ * @default true
93
+ */
94
+ faceMatchEnabled?: boolean;
85
95
  /**
86
96
  * External user identifier from merchant system
87
97
  * Optional parameter that will be returned with verification results
@@ -102,7 +112,7 @@ export interface VerificationOptions {
102
112
  autoReturn?: boolean;
103
113
  /**
104
114
  * UI language override for this verification
105
- * Supported: 'en', 'de', 'es', 'fr'
115
+ * Supported: 'en', 'de', 'es', 'fr', 'pt', 'it'
106
116
  * Takes precedence over SDKConfig.language
107
117
  */
108
118
  language?: string;
@@ -116,7 +126,7 @@ export interface VerificationResult {
116
126
  * Binary result: 'verified' or 'failed'
117
127
  * Full details available via server-side API
118
128
  */
119
- status: 'verified' | 'failed';
129
+ status: 'verified' | 'failed' | 'cancelled';
120
130
  /**
121
131
  * Timestamp when verification completed (milliseconds since epoch)
122
132
  */
@@ -134,6 +144,7 @@ export interface StatePayload {
134
144
  cancelUrl?: string;
135
145
  challengeAge?: number;
136
146
  verificationMode?: 'L1' | 'L2';
147
+ faceMatchEnabled?: boolean;
137
148
  hasOverrides?: boolean;
138
149
  externalUserId?: string;
139
150
  timestamp: number;
@@ -187,6 +198,7 @@ export interface CreateSessionRequest {
187
198
  cancelUrl?: string;
188
199
  challengeAge?: number;
189
200
  verificationMode?: 'L1' | 'L2';
201
+ faceMatchEnabled?: boolean;
190
202
  merchantName?: string;
191
203
  externalUserId?: string;
192
204
  }
@@ -1,8 +1,31 @@
1
1
  /**
2
2
  * Environment utilities with security enforcement.
3
3
  */
4
- import type { UrlConfig } from '../core/VerificationSDK';
4
+ import type { UrlConfig, BrandUrls } from '../core/VerificationSDK';
5
5
  export declare function getEnvironmentUrl(environment: 'production' | 'staging', urls: UrlConfig): string;
6
6
  export declare function getApiUrl(environment: 'production' | 'staging', urls: UrlConfig): string;
7
- export declare function detectEnvironment(): 'production' | 'staging';
7
+ /**
8
+ * Derive the set of first-party Verity staging DNS zones from the brand URL
9
+ * config (never hardcoded). Any production zone is excluded so that a
10
+ * production host can never be misclassified as first-party staging.
11
+ *
12
+ * For each current brand config this yields its internal staging parent zone.
13
+ */
14
+ export declare function getFirstPartyStagingHosts(brandUrls: BrandUrls): string[];
15
+ /**
16
+ * True only when `hostname` is, or is a subdomain of, a first-party Verity
17
+ * staging zone for the given brand. Customer-owned hostnames (including ones
18
+ * containing "staging"/"stage") return false.
19
+ */
20
+ export declare function isFirstPartyStagingHost(hostname: string, brandUrls: BrandUrls): boolean;
21
+ /**
22
+ * Resolve the SDK environment.
23
+ *
24
+ * Precedence:
25
+ * 1. Explicit 'staging' / 'production' is honored as-is.
26
+ * 2. Any other explicit (truthy) value fails closed to 'production'.
27
+ * 3. No explicit value -> 'staging' only on a first-party Verity staging
28
+ * host, otherwise 'production'.
29
+ */
30
+ export declare function resolveEnvironment(explicitEnvironment: string | undefined, brandUrls: BrandUrls): 'production' | 'staging';
8
31
  export declare function validateEnvironmentSecurity(environment: 'production' | 'staging', urls: UrlConfig, logLabel?: string): void;
@@ -10,6 +10,14 @@ export declare const STATE_EXPIRY_MS = 600000;
10
10
  export interface ValidationContext {
11
11
  brandName: string;
12
12
  docsUrl: string;
13
+ /**
14
+ * Resolved SDK environment, supplied by the centralized resolver in
15
+ * VerificationSDK (see utils/environment.resolveEnvironment). Validation never
16
+ * detects the environment from the hostname itself, so the staging-vs-production
17
+ * decision cannot drift between modules. Defaults to 'production' when omitted
18
+ * (e.g. direct unit tests that call validateConfig without the SDK).
19
+ */
20
+ environment?: 'production' | 'staging';
13
21
  }
14
22
  export declare function validateConfig(config: SDKConfig, context: ValidationContext): void;
15
23
  export declare function validateSessionId(sessionId: string): void;