@privateav/sdk 3.4.11 → 3.5.0

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
@@ -77,6 +77,7 @@ That's it! The SDK handles session creation automatically.
77
77
  | defaultVerificationMode | string | No | `'L1'` or `'L2'` |
78
78
  | onComplete | function | No | Callback for new-tab mode |
79
79
  | onCancel | function | No | Called when user closes popup (new-tab mode). Return `false` to suppress automatic `cancelUrl` redirect. |
80
+ | language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`). Can be overridden per verification. |
80
81
  | onError | function | No | Error handler |
81
82
 
82
83
  ## Verification Options
@@ -89,7 +90,8 @@ await sp.verify({
89
90
  verificationMode: 'L2', // Force ID verification for this session
90
91
  externalUserId: 'user-123', // Your user ID (returned in webhooks)
91
92
  skipIntro: true, // Skip intro screen
92
- autoReturn: true // Auto-redirect after success
93
+ autoReturn: true, // Auto-redirect after success
94
+ language: 'es' // Override UI language for this session
93
95
  });
94
96
  ```
95
97
 
@@ -157,7 +159,7 @@ app.get('/age-verified', async (req, res) => {
157
159
 
158
160
  // Validate with your SECRET key (sk_...)
159
161
  const response = await fetch(
160
- `https://api.safepassage.live/api/v1/sessions/${sessionId}`,
162
+ `https://api.privateav.com/api/v1/sessions/${sessionId}`,
161
163
  {
162
164
  headers: {
163
165
  'Authorization': `Bearer ${process.env.PRIVATEAV_SECRET_KEY}`
@@ -313,7 +315,7 @@ PrivateAV uses two types of API keys:
313
315
  | Public Key | `pk_` | Client-side SDK (this package) |
314
316
  | Secret Key | `sk_` | Server-side validation only |
315
317
 
316
- > **Important**: This SDK only works with public keys (`pk_`). For server-side integrations using secret keys, use the [Direct API](https://docs.safepassage.live/api) instead.
318
+ > **Important**: This SDK only works with public keys (`pk_`). For server-side integrations using secret keys, use the [Direct API](https://docs.privateav.com/api) instead.
317
319
 
318
320
  ## Browser Support
319
321
 
@@ -355,6 +357,6 @@ The SDK now creates sessions automatically via the API when using public keys.
355
357
 
356
358
  ## Support
357
359
 
358
- - [Documentation](https://docs.safepassage.live)
359
- - [API Reference](https://docs.safepassage.live/api)
360
- - [Dashboard](https://portal.safepassage.live)
360
+ - [Documentation](https://docs.privateav.com)
361
+ - [API Reference](https://docs.privateav.com/api)
362
+ - [Dashboard](https://portal.privateav.com)
@@ -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.4.9";
10
+ export declare const VERSION = "3.5.0";
11
11
  export type { SDKConfig, VerificationOptions, VerificationResult, SessionValidationResponse, SessionCreationResponse, CreateSessionRequest, };
12
12
  export default PrivateAV;
package/index.js CHANGED
@@ -98,6 +98,12 @@ function enforceHTTPS(environment, logLabel = "SDK") {
98
98
  function validateReturnUrl(url, environment, _logLabel = "SDK") {
99
99
  try {
100
100
  const parsed = new URL(url);
101
+ if (parsed.protocol === "file:") {
102
+ return {
103
+ isValid: false,
104
+ 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."
105
+ };
106
+ }
101
107
  if (parsed.protocol !== "https:") {
102
108
  const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
103
109
  if (!isLocalhost) {
@@ -611,6 +617,7 @@ var _VerificationSDK = class _VerificationSDK {
611
617
  this.getHmacSecret(),
612
618
  this.brandConstants.name
613
619
  );
620
+ const language = options.language || this.config.language;
614
621
  if (this.lastVerifyUrl) {
615
622
  try {
616
623
  const resolvedVerifyUrl = this.applyLocalVerifyOverride(this.lastVerifyUrl);
@@ -623,6 +630,9 @@ var _VerificationSDK = class _VerificationSDK {
623
630
  if (options.autoReturn) {
624
631
  url.searchParams.set("auto_return", "true");
625
632
  }
633
+ if (language) {
634
+ url.searchParams.set("lang", language);
635
+ }
626
636
  return url.toString();
627
637
  } catch (e) {
628
638
  }
@@ -634,6 +644,9 @@ var _VerificationSDK = class _VerificationSDK {
634
644
  if (options.autoReturn) {
635
645
  params.set("auto_return", "true");
636
646
  }
647
+ if (language) {
648
+ params.set("lang", language);
649
+ }
637
650
  return `${baseUrl}/?${params.toString()}`;
638
651
  }
639
652
  /**
@@ -952,7 +965,8 @@ var _VerificationSDK = class _VerificationSDK {
952
965
  const errorData = await response.json().catch(() => ({}));
953
966
  const errorCode = errorData == null ? void 0 : errorData.code;
954
967
  if (this.isBillingBlockError(errorCode)) {
955
- this.openBillingBlockPage(errorCode, errorData == null ? void 0 : errorData.portalUrl);
968
+ const language = options.language || this.config.language;
969
+ this.openBillingBlockPage(errorCode, errorData == null ? void 0 : errorData.portalUrl, language);
956
970
  }
957
971
  throw new Error(
958
972
  `Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ""}`
@@ -997,7 +1011,7 @@ var _VerificationSDK = class _VerificationSDK {
997
1011
  isBillingBlockError(code) {
998
1012
  return code === "SUBSCRIPTION_REQUIRED" || code === "PLAN_LIMIT_REACHED" || code === "SANDBOX_LIMIT_REACHED";
999
1013
  }
1000
- openBillingBlockPage(code, portalUrl) {
1014
+ openBillingBlockPage(code, portalUrl, language) {
1001
1015
  var _a, _b, _c, _d;
1002
1016
  try {
1003
1017
  const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
@@ -1007,6 +1021,10 @@ var _VerificationSDK = class _VerificationSDK {
1007
1021
  if (portalUrl) {
1008
1022
  url.searchParams.set("portalUrl", portalUrl);
1009
1023
  }
1024
+ const effectiveLanguage = language || this.config.language;
1025
+ if (effectiveLanguage) {
1026
+ url.searchParams.set("lang", effectiveLanguage);
1027
+ }
1010
1028
  if (this.config.mode === "new-tab") {
1011
1029
  const target = this.config.newTabTarget || "popup";
1012
1030
  const popup = target === "tab" ? window.open(url.toString(), "_blank") : window.open(
@@ -1129,7 +1147,7 @@ var PrivateAV = class extends VerificationSDK {
1129
1147
  super(config, BRAND_URLS, BRAND_CONSTANTS);
1130
1148
  }
1131
1149
  };
1132
- var VERSION = "3.4.9";
1150
+ var VERSION = "3.5.0";
1133
1151
  PrivateAV.VERSION = VERSION;
1134
1152
  if (typeof window !== "undefined") {
1135
1153
  setupPolyfills();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privateav/sdk",
3
- "version": "3.4.11",
3
+ "version": "3.5.0",
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.4.11 */
2
- "use strict";var PrivateAVSDK=(()=>{var y=Object.defineProperty,dt=Object.defineProperties,pt=Object.getOwnPropertyDescriptor,gt=Object.getOwnPropertyDescriptors,ut=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,t,e)=>t in n?y(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e,u=(n,t)=>{for(var e in t||(t={}))T.call(t,e)&&B(n,e,t[e]);if(v)for(var e of v(t))W.call(t,e)&&B(n,e,t[e]);return n},S=(n,t)=>dt(n,gt(t));var H=(n,t)=>{var e={};for(var r in n)T.call(n,r)&&t.indexOf(r)<0&&(e[r]=n[r]);if(n!=null&&v)for(var r of v(n))t.indexOf(r)<0&&W.call(n,r)&&(e[r]=n[r]);return e};var A=(n,t)=>()=>(n&&(t=n(n=0)),t);var F=(n,t)=>{for(var e in t)y(n,e,{get:t[e],enumerable:!0})},ft=(n,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ut(t))!T.call(n,i)&&i!==e&&y(n,i,{get:()=>t[i],enumerable:!(r=pt(t,i))||r.enumerable});return n};var ht=n=>ft(y({},"__esModule",{value:!0}),n);function mt(n,t){return t.includes(n)}function G(n,t,e=[],r="SDK"){var o;let{origin:i}=n;return mt(i,t)||e.length>0&&e.some(a=>{if(a.startsWith("*.")){let l=a.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:t,allowedCustomOrigins:e,eventType:(o=n.data)==null?void 0:o.type}),!1)}function J(n,t,e,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[e,r]:[e]).includes(i.type)?!i.sessionId||i.sessionId!==t?{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,t="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${t} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,t,e="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${t}`};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 p(n,t,e="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${e} Security Event: ${n}`,u(u({},r),t))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(t,e="SDK"){let r=Date.now(),o=(this.attempts.get(t)||[]).filter(s=>r-s<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${e} Security: Rate limit exceeded for ${t}`),!1):(o.push(r),this.attempts.set(t,o),!0)}reset(t){this.attempts.delete(t)}},Y=new P});var Q={};F(Q,{createSignedState:()=>vt,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>yt,verifyHMAC:()=>z});async function V(n,t){let e=new TextEncoder,r=e.encode(t),i=e.encode(n),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(s)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function z(n,t,e){try{let r=await V(n,e);return wt(t,r)}catch(r){return!1}}function wt(n,t){if(n.length!==t.length)return!1;let e=0;for(let r=0;r<n.length;r++)e|=n.charCodeAt(r)^t.charCodeAt(r);return e===0}function Z(n=32){let t=new Uint8Array(n);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}async function vt(n,t){let e=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(e),i=await V(r,t);return btoa(JSON.stringify({data:e,signature:i}))}async function yt(n,t,e=et,r="SDK"){try{let o=atob(n),s=JSON.parse(o);if(!s.data||!s.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:l}=s,d=JSON.stringify(a);if(!await z(d,l,t))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>e)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:e}),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 tt=A(()=>{"use strict";L()});function ot(n,t){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>it)throw new Error(`apiKey exceeds maximum length of ${it} characters`);if(!St.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: ${t.docsUrl}/server-side-sessions`);if(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let e=Ut(),r=x(n.returnUrl,e,t.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=x(n.cancelUrl,e,t.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<nt)throw new Error(`defaultChallengeAge must be at least ${nt}`);if(n.defaultChallengeAge>rt)throw new Error(`defaultChallengeAge cannot exceed ${rt}`)}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 Ut(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function st(n,t,e,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(tt(),Q));return i(n,e)}var nt,rt,U,it,et,St,L=A(()=>{"use strict";R();nt=25,rt=150,U=2048,it=128,et=6e5,St=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ct={};F(Ct,{PrivateAV:()=>m,VERSION:()=>ct,default:()=>bt});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 t=Array.from(n).map(e=>e.toString(16).padStart(2,"0")).join("");return[t.slice(0,8),t.slice(8,12),t.slice(12,16),t.slice(16,20),t.slice(20,32)].join("-")})}function j(n="SDK"){let t=[];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||t.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||t.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),t.length>0&&console.warn(`${n} Browser Compatibility:`,t.join("; "))}L();function E(n,t){let e=t.verifyUiUrl;if(!e||!e.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return e}function Et(n,t){let e=t.apiUrl;if(!e||!e.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return e}function at(n,t,e="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${e} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${e} Warning: HTTPS strongly recommended in staging environment`);break}try{E(n,t),Et(n,t)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}R();var C=class C{constructor(t,e,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=e,this.brandConstants=r,ot(t,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=t.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},t),{environment:i,mode:t.mode||"redirect",newTabTarget:t.newTabTarget||"popup"}),at(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(t={}){var i,o,s,a,l,d;let e=this.isPublicKey(),r;if(e)r=await this.createInternalSession(t);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 c=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=(s=this.config).onError)==null||a.call(s,c),c}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=t.externalUserId||null;try{let c=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(c,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=(l=this.config).onError)==null||d.call(l,f),f}let g=await this.buildVerificationUrl(t,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(c){throw this.unlockVerification(),c}}async buildVerificationUrl(t,e){var d;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=t.challengeAge!==void 0,o=t.verificationMode!==void 0,s=i||o,a=await st({merchantId:this.config.apiKey,sessionId:e,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:t.challengeAge||this.config.defaultChallengeAge,verificationMode:t.verificationMode||this.config.defaultVerificationMode,hasOverrides:s,externalUserId:t.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:(d=this.lastSandboxMode)!=null?d:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let c=this.applyLocalVerifyOverride(this.lastVerifyUrl),g=new URL(c);return g.searchParams.set("state",a),g.searchParams.set("mode",this.config.mode),t.skipIntro&&g.searchParams.set("skip_intro","true"),t.autoReturn&&g.searchParams.set("auto_return","true"),g.toString()}catch(c){}let l=new URLSearchParams({state:a,sessionId:e,mode:this.config.mode});return t.skipIntro&&l.set("skip_intro","true"),t.autoReturn&&l.set("auto_return","true"),`${r}/?${l.toString()}`}redirect(t){window.location.href=t}openNewTab(t,e){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(t,"_blank"):this.popupWindow=window.open(t,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(t),s=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=c=>{var D,M,_,O,$,N,K;let g=(D=c.data)==null?void 0:D.type;if(!g||typeof g!="string"||!(a?[s,a]:[s]).includes(g))return;if(!G(c,i,o,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(M=c.data)==null?void 0:M.type},this.brandConstants.name);return}let I=J(c,e,s,a);if(!I.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:c.origin,sessionId:e.substring(0,8)+"...",messageType:(_=c.data)==null?void 0:_.type},this.brandConstants.name);return}let h=c.data.status;if(h==="cancelled"){this.handleCancellation(e,"postmessage");return}let w={sessionId:c.data.sessionId,status:h,timestamp:c.data.timestamp,externalUserId:c.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:w.status,sessionId:e.substring(0,8)+"...",origin:c.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&&(p("POPUP_CLOSED_BY_USER",{sessionId:e.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(e,"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 t=window.history.pushState;window.history.pushState=(...e)=>(this.cleanup(),this.unlockVerification(),t.apply(window.history,e))}}detectEnvironment(){let t=window.location.hostname;return t.includes("staging")||t.includes("stage")?"staging":"production"}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(t={}){let e=t.closePopup!==!1;this.popupWindow&&(e&&!this.popupWindow.closed&&this.popupWindow.close(),(e||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(t,e){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,p("VERIFICATION_CANCELLED",{source:e,sessionId:t.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:t.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(t)}redirectToCancelUrl(t){if(this.config.cancelUrl)try{let e=decodeURIComponent(this.config.cancelUrl),r=new URL(e);r.searchParams.set("sessionId",t),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(e){p("CANCEL_REDIRECT_FAILED",{error:e instanceof Error?e.message:String(e),sessionId:t.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(t){var e,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:t.challengeAge,verificationMode:t.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:t.externalUserId})});if(!o.ok){let l=await o.json().catch(()=>({})),d=l==null?void 0:l.code;throw this.isBillingBlockError(d)&&this.openBillingBlockPage(d,l==null?void 0:l.portalUrl),new Error(`Failed to create session: ${o.status} ${o.statusText}. ${l.message||""}`)}let s=await o.json(),a=s.sessionId;if(!a)throw new Error("Server did not return a sessionId");return s.verifyUrl&&(this.lastVerifyUrl=s.verifyUrl),s.sessionToken&&(this.lastSessionToken=s.sessionToken),s.handoffToken&&(this.temporaryHandoffToken=s.handoffToken),typeof s.sandboxMode=="boolean"?this.lastSandboxMode=s.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:a.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),a}catch(i){let o=i instanceof Error?i.message:String(i);throw p("INTERNAL_SESSION_FAILED",{error:o,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(e=this.config).onError)==null||r.call(e,i),new Error(`Failed to create verification session: ${o}`)}}isBillingBlockError(t){return t==="SUBSCRIPTION_REQUIRED"||t==="PLAN_LIMIT_REACHED"||t==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(t,e){var r,i,o,s;try{let a=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),l=this.applyLocalVerifyOverride(a),d=new URL(l);if(d.searchParams.set("blocked",t),e&&d.searchParams.set("portalUrl",e),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(d.toString(),"_blank"):window.open(d.toString(),this.brandConstants.popupName,"width=600,height=700"))||(i=(r=this.config).onError)==null||i.call(r,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(d.toString())}catch(a){let l=a instanceof Error?a.message:String(a);(s=(o=this.config).onError)==null||s.call(o,new Error(`Failed to open billing notice: ${l}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(t){let e=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(t||null);return r&&e.add(r),i&&e.add(i),Array.from(e)}getLocalOrigin(t){if(!t)return null;try{let e=new URL(t);if(C.LOCAL_HOSTNAMES.has(e.hostname))return e.origin}catch(e){return null}return null}applyLocalVerifyOverride(t){let e=this.getLocalOrigin(this.config.verifyUrl||null);if(!e)return t;try{let r=new URL(e),i=new URL(t);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return t}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var b=C;var lt={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"]}},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 b{constructor(t){super(t,lt,k)}},ct="3.4.9";m.VERSION=ct;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var bt=m;return ht(Ct);})();
1
+ /* PrivateAV SDK v3.5.0 */
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.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"]}},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.0";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
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.4.11 */
2
- "use strict";var PrivateAVSDK=(()=>{var y=Object.defineProperty,dt=Object.defineProperties,pt=Object.getOwnPropertyDescriptor,gt=Object.getOwnPropertyDescriptors,ut=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,t,e)=>t in n?y(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e,u=(n,t)=>{for(var e in t||(t={}))T.call(t,e)&&B(n,e,t[e]);if(v)for(var e of v(t))W.call(t,e)&&B(n,e,t[e]);return n},S=(n,t)=>dt(n,gt(t));var H=(n,t)=>{var e={};for(var r in n)T.call(n,r)&&t.indexOf(r)<0&&(e[r]=n[r]);if(n!=null&&v)for(var r of v(n))t.indexOf(r)<0&&W.call(n,r)&&(e[r]=n[r]);return e};var A=(n,t)=>()=>(n&&(t=n(n=0)),t);var F=(n,t)=>{for(var e in t)y(n,e,{get:t[e],enumerable:!0})},ft=(n,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ut(t))!T.call(n,i)&&i!==e&&y(n,i,{get:()=>t[i],enumerable:!(r=pt(t,i))||r.enumerable});return n};var ht=n=>ft(y({},"__esModule",{value:!0}),n);function mt(n,t){return t.includes(n)}function G(n,t,e=[],r="SDK"){var o;let{origin:i}=n;return mt(i,t)||e.length>0&&e.some(a=>{if(a.startsWith("*.")){let l=a.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:t,allowedCustomOrigins:e,eventType:(o=n.data)==null?void 0:o.type}),!1)}function J(n,t,e,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[e,r]:[e]).includes(i.type)?!i.sessionId||i.sessionId!==t?{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,t="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${t} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,t,e="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${t}`};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 p(n,t,e="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${e} Security Event: ${n}`,u(u({},r),t))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(t,e="SDK"){let r=Date.now(),o=(this.attempts.get(t)||[]).filter(s=>r-s<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${e} Security: Rate limit exceeded for ${t}`),!1):(o.push(r),this.attempts.set(t,o),!0)}reset(t){this.attempts.delete(t)}},Y=new P});var Q={};F(Q,{createSignedState:()=>vt,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>yt,verifyHMAC:()=>z});async function V(n,t){let e=new TextEncoder,r=e.encode(t),i=e.encode(n),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(s)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function z(n,t,e){try{let r=await V(n,e);return wt(t,r)}catch(r){return!1}}function wt(n,t){if(n.length!==t.length)return!1;let e=0;for(let r=0;r<n.length;r++)e|=n.charCodeAt(r)^t.charCodeAt(r);return e===0}function Z(n=32){let t=new Uint8Array(n);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}async function vt(n,t){let e=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(e),i=await V(r,t);return btoa(JSON.stringify({data:e,signature:i}))}async function yt(n,t,e=et,r="SDK"){try{let o=atob(n),s=JSON.parse(o);if(!s.data||!s.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:l}=s,d=JSON.stringify(a);if(!await z(d,l,t))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>e)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:e}),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 tt=A(()=>{"use strict";L()});function ot(n,t){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>it)throw new Error(`apiKey exceeds maximum length of ${it} characters`);if(!St.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: ${t.docsUrl}/server-side-sessions`);if(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let e=Ut(),r=x(n.returnUrl,e,t.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=x(n.cancelUrl,e,t.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<nt)throw new Error(`defaultChallengeAge must be at least ${nt}`);if(n.defaultChallengeAge>rt)throw new Error(`defaultChallengeAge cannot exceed ${rt}`)}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 Ut(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function st(n,t,e,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(tt(),Q));return i(n,e)}var nt,rt,U,it,et,St,L=A(()=>{"use strict";R();nt=25,rt=150,U=2048,it=128,et=6e5,St=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ct={};F(Ct,{PrivateAV:()=>m,VERSION:()=>ct,default:()=>bt});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 t=Array.from(n).map(e=>e.toString(16).padStart(2,"0")).join("");return[t.slice(0,8),t.slice(8,12),t.slice(12,16),t.slice(16,20),t.slice(20,32)].join("-")})}function j(n="SDK"){let t=[];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||t.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||t.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),t.length>0&&console.warn(`${n} Browser Compatibility:`,t.join("; "))}L();function E(n,t){let e=t.verifyUiUrl;if(!e||!e.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return e}function Et(n,t){let e=t.apiUrl;if(!e||!e.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return e}function at(n,t,e="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${e} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${e} Warning: HTTPS strongly recommended in staging environment`);break}try{E(n,t),Et(n,t)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}R();var C=class C{constructor(t,e,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=e,this.brandConstants=r,ot(t,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=t.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},t),{environment:i,mode:t.mode||"redirect",newTabTarget:t.newTabTarget||"popup"}),at(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(t={}){var i,o,s,a,l,d;let e=this.isPublicKey(),r;if(e)r=await this.createInternalSession(t);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 c=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=(s=this.config).onError)==null||a.call(s,c),c}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=t.externalUserId||null;try{let c=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(c,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=(l=this.config).onError)==null||d.call(l,f),f}let g=await this.buildVerificationUrl(t,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(c){throw this.unlockVerification(),c}}async buildVerificationUrl(t,e){var d;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=t.challengeAge!==void 0,o=t.verificationMode!==void 0,s=i||o,a=await st({merchantId:this.config.apiKey,sessionId:e,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:t.challengeAge||this.config.defaultChallengeAge,verificationMode:t.verificationMode||this.config.defaultVerificationMode,hasOverrides:s,externalUserId:t.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:(d=this.lastSandboxMode)!=null?d:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let c=this.applyLocalVerifyOverride(this.lastVerifyUrl),g=new URL(c);return g.searchParams.set("state",a),g.searchParams.set("mode",this.config.mode),t.skipIntro&&g.searchParams.set("skip_intro","true"),t.autoReturn&&g.searchParams.set("auto_return","true"),g.toString()}catch(c){}let l=new URLSearchParams({state:a,sessionId:e,mode:this.config.mode});return t.skipIntro&&l.set("skip_intro","true"),t.autoReturn&&l.set("auto_return","true"),`${r}/?${l.toString()}`}redirect(t){window.location.href=t}openNewTab(t,e){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(t,"_blank"):this.popupWindow=window.open(t,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(t),s=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=c=>{var D,M,_,O,$,N,K;let g=(D=c.data)==null?void 0:D.type;if(!g||typeof g!="string"||!(a?[s,a]:[s]).includes(g))return;if(!G(c,i,o,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(M=c.data)==null?void 0:M.type},this.brandConstants.name);return}let I=J(c,e,s,a);if(!I.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:c.origin,sessionId:e.substring(0,8)+"...",messageType:(_=c.data)==null?void 0:_.type},this.brandConstants.name);return}let h=c.data.status;if(h==="cancelled"){this.handleCancellation(e,"postmessage");return}let w={sessionId:c.data.sessionId,status:h,timestamp:c.data.timestamp,externalUserId:c.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:w.status,sessionId:e.substring(0,8)+"...",origin:c.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&&(p("POPUP_CLOSED_BY_USER",{sessionId:e.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(e,"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 t=window.history.pushState;window.history.pushState=(...e)=>(this.cleanup(),this.unlockVerification(),t.apply(window.history,e))}}detectEnvironment(){let t=window.location.hostname;return t.includes("staging")||t.includes("stage")?"staging":"production"}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(t={}){let e=t.closePopup!==!1;this.popupWindow&&(e&&!this.popupWindow.closed&&this.popupWindow.close(),(e||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(t,e){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,p("VERIFICATION_CANCELLED",{source:e,sessionId:t.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:t.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(t)}redirectToCancelUrl(t){if(this.config.cancelUrl)try{let e=decodeURIComponent(this.config.cancelUrl),r=new URL(e);r.searchParams.set("sessionId",t),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(e){p("CANCEL_REDIRECT_FAILED",{error:e instanceof Error?e.message:String(e),sessionId:t.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(t){var e,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:t.challengeAge,verificationMode:t.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:t.externalUserId})});if(!o.ok){let l=await o.json().catch(()=>({})),d=l==null?void 0:l.code;throw this.isBillingBlockError(d)&&this.openBillingBlockPage(d,l==null?void 0:l.portalUrl),new Error(`Failed to create session: ${o.status} ${o.statusText}. ${l.message||""}`)}let s=await o.json(),a=s.sessionId;if(!a)throw new Error("Server did not return a sessionId");return s.verifyUrl&&(this.lastVerifyUrl=s.verifyUrl),s.sessionToken&&(this.lastSessionToken=s.sessionToken),s.handoffToken&&(this.temporaryHandoffToken=s.handoffToken),typeof s.sandboxMode=="boolean"?this.lastSandboxMode=s.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:a.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),a}catch(i){let o=i instanceof Error?i.message:String(i);throw p("INTERNAL_SESSION_FAILED",{error:o,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(e=this.config).onError)==null||r.call(e,i),new Error(`Failed to create verification session: ${o}`)}}isBillingBlockError(t){return t==="SUBSCRIPTION_REQUIRED"||t==="PLAN_LIMIT_REACHED"||t==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(t,e){var r,i,o,s;try{let a=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),l=this.applyLocalVerifyOverride(a),d=new URL(l);if(d.searchParams.set("blocked",t),e&&d.searchParams.set("portalUrl",e),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(d.toString(),"_blank"):window.open(d.toString(),this.brandConstants.popupName,"width=600,height=700"))||(i=(r=this.config).onError)==null||i.call(r,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(d.toString())}catch(a){let l=a instanceof Error?a.message:String(a);(s=(o=this.config).onError)==null||s.call(o,new Error(`Failed to open billing notice: ${l}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(t){let e=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(t||null);return r&&e.add(r),i&&e.add(i),Array.from(e)}getLocalOrigin(t){if(!t)return null;try{let e=new URL(t);if(C.LOCAL_HOSTNAMES.has(e.hostname))return e.origin}catch(e){return null}return null}applyLocalVerifyOverride(t){let e=this.getLocalOrigin(this.config.verifyUrl||null);if(!e)return t;try{let r=new URL(e),i=new URL(t);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return t}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var b=C;var lt={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"]}},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 b{constructor(t){super(t,lt,k)}},ct="3.4.9";m.VERSION=ct;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var bt=m;return ht(Ct);})();
1
+ /* PrivateAV SDK v3.5.0 */
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.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"]}},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.0";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
3
3
  if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
package/types/base.d.ts CHANGED
@@ -58,6 +58,12 @@ export interface SDKConfig {
58
58
  * Return false to suppress automatic cancelUrl redirect.
59
59
  */
60
60
  onCancel?: () => boolean | void;
61
+ /**
62
+ * Default UI language for verification screens
63
+ * Supported: 'en', 'de', 'es', 'fr'
64
+ * Can be overridden per verification call
65
+ */
66
+ language?: string;
61
67
  /**
62
68
  * Callback for errors
63
69
  */
@@ -94,6 +100,12 @@ export interface VerificationOptions {
94
100
  * @default false
95
101
  */
96
102
  autoReturn?: boolean;
103
+ /**
104
+ * UI language override for this verification
105
+ * Supported: 'en', 'de', 'es', 'fr'
106
+ * Takes precedence over SDKConfig.language
107
+ */
108
+ language?: string;
97
109
  }
98
110
  export interface VerificationResult {
99
111
  /**
@@ -156,9 +168,19 @@ export interface SessionValidationResponse {
156
168
  expiresAt: string;
157
169
  }
158
170
  export interface SessionCreationResponse {
171
+ sessionId?: string;
159
172
  sessionToken: string;
160
173
  verifyUrl: string;
161
174
  expiresAt: string;
175
+ testMode?: boolean;
176
+ sandboxMode?: boolean;
177
+ externalUserId?: string;
178
+ handoffToken?: string;
179
+ billingBlock?: {
180
+ code: 'SUBSCRIPTION_REQUIRED' | 'PLAN_LIMIT_REACHED' | 'SANDBOX_LIMIT_REACHED';
181
+ message: string;
182
+ portalUrl?: string;
183
+ };
162
184
  }
163
185
  export interface CreateSessionRequest {
164
186
  returnUrl: string;